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.
This commit is contained in:
kre 2017-06-07 05:08:32 +00:00
parent fd38bbe2e4
commit 727a69dc1d
21 changed files with 629 additions and 158 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: arith_token.c,v 1.4 2017/05/29 22:54:07 kre Exp $ */
/* $NetBSD: arith_token.c,v 1.5 2017/06/07 05:08:32 kre Exp $ */
/*-
* Copyright (c) 2002
@ -39,7 +39,7 @@
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: arith_token.c,v 1.4 2017/05/29 22:54:07 kre Exp $");
__RCSID("$NetBSD: arith_token.c,v 1.5 2017/06/07 05:08:32 kre Exp $");
#endif /* not lint */
#include <inttypes.h>
@ -98,6 +98,7 @@ arith_token(void)
* and nothing else does. They continue for the
* longest unbroken sequence of alphanumerics ( + _ )
*/
arith_var_lno = arith_lno;
p = buf;
while (buf++, is_in_name(*buf))
;
@ -115,6 +116,7 @@ arith_token(void)
* operator, white space, or an error.
*/
case '\n':
arith_lno++;
VTRACE(DBG_ARITH, ("Arith: newline\n"));
/* FALLTHROUGH */
case ' ':

View File

@ -1,4 +1,4 @@
/* $NetBSD: arith_tokens.h,v 1.1 2017/03/20 11:26:07 kre Exp $ */
/* $NetBSD: arith_tokens.h,v 1.2 2017/06/07 05:08:32 kre Exp $ */
/*-
* Copyright (c) 1993
@ -112,4 +112,6 @@ union a_token_val {
extern union a_token_val a_t_val;
extern int arith_lno, arith_var_lno;
int arith_token(void);

View File

@ -1,4 +1,4 @@
/* $NetBSD: arithmetic.c,v 1.2 2017/05/29 22:54:07 kre Exp $ */
/* $NetBSD: arithmetic.c,v 1.3 2017/06/07 05:08:32 kre Exp $ */
/*-
* Copyright (c) 1993
@ -39,7 +39,7 @@
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: arithmetic.c,v 1.2 2017/05/29 22:54:07 kre Exp $");
__RCSID("$NetBSD: arithmetic.c,v 1.3 2017/06/07 05:08:32 kre Exp $");
#endif /* not lint */
#include <limits.h>
@ -47,6 +47,7 @@ __RCSID("$NetBSD: arithmetic.c,v 1.2 2017/05/29 22:54:07 kre Exp $");
#include <inttypes.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "shell.h"
#include "arithmetic.h"
@ -71,6 +72,8 @@ union a_token_val a_t_val;
static int last_token;
int arith_lno, arith_var_lno;
#define ARITH_PRECEDENCE(op, prec) [op - ARITH_BINOP_MIN] = prec
static const char prec[ARITH_BINOP_MAX - ARITH_BINOP_MIN] = {
@ -109,8 +112,15 @@ arith_lookupvarint(char *varname)
const char *str;
char *p;
intmax_t result;
const int oln = line_number;
VTRACE(DBG_ARITH, ("Arith var lookup(\"%s\") with lno=%d\n", varname,
arith_var_lno));
line_number = arith_var_lno;
str = lookupvar(varname);
line_number = oln;
if (uflag && str == NULL)
arith_err("variable not set");
if (str == NULL || *str == '\0')
@ -365,14 +375,33 @@ assignment(int var, int noeval)
}
intmax_t
arith(const char *s)
arith(const char *s, int lno)
{
struct stackmark smark;
intmax_t result;
const char *p;
int nls = 0;
setstackmark(&smark);
CTRACE(DBG_ARITH, ("Arith(\"%s\")\n", s));
arith_lno = lno;
CTRACE(DBG_ARITH, ("Arith(\"%s\", %d) @%d\n", s, lno, arith_lno));
/* check if it is possible we might reference LINENO */
p = s;
while ((p = strchr(p, 'L')) != NULL) {
if (p[1] == 'I' && p[2] == 'N') {
/* if it is possible, we need to correct airth_lno */
p = s;
while ((p = strchr(p, '\n')) != NULL)
nls++, p++;
VTRACE(DBG_ARITH, ("Arith found %d newlines\n", nls));
arith_lno -= nls;
break;
}
p++;
}
arith_buf = arith_startbuf = s;
@ -420,7 +449,7 @@ expcmd(int argc, char **argv)
} else
p = "";
i = arith(p);
i = arith(p, line_number);
out1fmt(ARITH_FORMAT_STR "\n", i);
return !i;

View File

@ -1,4 +1,4 @@
/* $NetBSD: arithmetic.h,v 1.1 2017/03/20 11:26:07 kre Exp $ */
/* $NetBSD: arithmetic.h,v 1.2 2017/06/07 05:08:32 kre Exp $ */
/*-
* Copyright (c) 1995
@ -39,4 +39,4 @@
#define ARITH_FORMAT_STR "%" PRIdMAX
intmax_t arith(const char *);
intmax_t arith(const char *, int);

View File

@ -1,4 +1,4 @@
/* $NetBSD: eval.c,v 1.142 2017/06/07 04:44:17 kre Exp $ */
/* $NetBSD: eval.c,v 1.143 2017/06/07 05:08:32 kre Exp $ */
/*-
* Copyright (c) 1993
@ -37,7 +37,7 @@
#if 0
static char sccsid[] = "@(#)eval.c 8.9 (Berkeley) 6/8/95";
#else
__RCSID("$NetBSD: eval.c,v 1.142 2017/06/07 04:44:17 kre Exp $");
__RCSID("$NetBSD: eval.c,v 1.143 2017/06/07 05:08:32 kre Exp $");
#endif
#endif /* not lint */
@ -217,7 +217,7 @@ evalstring(char *s, int flag)
struct stackmark smark;
setstackmark(&smark);
setinputstring(s, 1, atoi(line_num.text + line_num.name_len + 1));
setinputstring(s, 1, line_number);
while ((n = parsecmd(0)) != NEOF) {
TRACE(("evalstring: "); showtree(n));
@ -317,7 +317,9 @@ evaltree(union node *n, int flags)
evalcase(n, sflags);
break;
case NDEFUN:
defun(n->narg.text, n->narg.next);
CTRACE(DBG_EVAL, ("Defining fn %s @%d%s\n", n->narg.text,
n->narg.lineno, fnline1 ? " LINENO=1" : ""));
defun(n->narg.text, n->narg.next, n->narg.lineno);
exitstatus = 0;
break;
case NNOT:
@ -473,9 +475,11 @@ evalcase(union node *n, int flags)
setstackmark(&smark);
arglist.lastp = &arglist.list;
line_number = n->ncase.lineno;
expandarg(n->ncase.expr, &arglist, EXP_TILDE);
for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
line_number = patp->narg.lineno;
if (casematch(patp, arglist.list->text)) {
while (cp != NULL && evalskip == 0 &&
nflag == 0) {
@ -483,6 +487,7 @@ evalcase(union node *n, int flags)
ncp = cp->nclist.next;
else
ncp = NULL;
line_number = cp->nclist.lineno;
evaltree(cp->nclist.body,
ncp ? (flags | EV_MORE) : flags);
status = exitstatus;
@ -766,21 +771,25 @@ evalcommand(union node *cmd, int flgs, struct backcmd *backcmd)
char * volatile lastarg;
const char * volatile path = pathval();
volatile int temp_path;
const int savefuncline = funclinebase;
const int savefuncabs = funclineabs;
vforked = 0;
/* First expand the arguments. */
TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
TRACE(("evalcommand(%p, %d) called\n", cmd, flags));
setstackmark(&smark);
back_exitstatus = 0;
if (cmd != NULL)
set_lineno(cmd->ncmd.lineno);
line_number = cmd->ncmd.lineno;
arglist.lastp = &arglist.list;
varflag = 1;
/* Expand arguments, ignoring the initial 'name=value' ones */
for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
char *p = argp->narg.text;
line_number = argp->narg.lineno;
if (varflag && is_name(*p)) {
do {
p++;
@ -799,6 +808,8 @@ evalcommand(union node *cmd, int flgs, struct backcmd *backcmd)
varlist.lastp = &varlist.list;
for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
char *p = argp->narg.text;
line_number = argp->narg.lineno;
if (!is_name(*p))
break;
do
@ -879,6 +890,7 @@ evalcommand(union node *cmd, int flgs, struct backcmd *backcmd)
do {
int argsused, use_syspath;
find_command(argv[0], &cmdentry, cmd_flags, path);
if (cmdentry.cmdtype == CMDUNKNOWN) {
exitstatus = 127;
@ -1031,11 +1043,27 @@ normal_fork:
}
poplocalvars();
localvars = savelocalvars;
funclinebase = savefuncline;
funclineabs = savefuncabs;
handler = savehandler;
longjmp(handler->loc, 1);
}
savehandler = handler;
handler = &jmploc;
if (cmdentry.u.func) {
if (cmdentry.lno_frel)
funclinebase = cmdentry.lineno - 1;
else
funclinebase = 0;
funclineabs = cmdentry.lineno;
VTRACE(DBG_EVAL,
("function: node: %d '%s' # %d%s; funclinebase=%d\n",
cmdentry.u.func->type,
NODETYPENAME(cmdentry.u.func->type),
cmdentry.lineno, cmdentry.lno_frel?" (=1)":"",
funclinebase));
}
listmklocal(varlist.list, VEXPORT);
/* stop shell blowing its stack */
if (++funcnest > 1000)
@ -1045,6 +1073,8 @@ normal_fork:
INTOFF;
poplocalvars();
localvars = savelocalvars;
funclinebase = savefuncline;
funclineabs = savefuncabs;
freeparam(&shellparam);
shellparam = saveparam;
handler = savehandler;

View File

@ -1,4 +1,4 @@
/* $NetBSD: exec.c,v 1.48 2017/06/04 20:27:14 kre Exp $ */
/* $NetBSD: exec.c,v 1.49 2017/06/07 05:08:32 kre Exp $ */
/*-
* Copyright (c) 1991, 1993
@ -37,7 +37,7 @@
#if 0
static char sccsid[] = "@(#)exec.c 8.4 (Berkeley) 6/8/95";
#else
__RCSID("$NetBSD: exec.c,v 1.48 2017/06/04 20:27:14 kre Exp $");
__RCSID("$NetBSD: exec.c,v 1.49 2017/06/07 05:08:32 kre Exp $");
#endif
#endif /* not lint */
@ -91,6 +91,8 @@ struct tblentry {
union param param; /* definition of builtin function */
short cmdtype; /* index identifying command */
char rehash; /* if set, cd done since entry created */
char fn_ln1; /* for functions, LINENO from 1 */
int lineno; /* for functions abs LINENO of definition */
char cmdname[ARB]; /* name of command */
};
@ -676,6 +678,8 @@ success:
if (cmdp) {
cmdp->rehash = 0;
entry->cmdtype = cmdp->cmdtype;
entry->lineno = cmdp->lineno;
entry->lno_frel = cmdp->fn_ln1;
entry->u = cmdp->param;
} else
entry->cmdtype = CMDUNKNOWN;
@ -971,6 +975,8 @@ addcmdentry(char *name, struct cmdentry *entry)
freefunc(cmdp->param.func);
}
cmdp->cmdtype = entry->cmdtype;
cmdp->lineno = entry->lineno;
cmdp->fn_ln1 = entry->lno_frel;
cmdp->param = entry->u;
}
INTON;
@ -982,12 +988,14 @@ addcmdentry(char *name, struct cmdentry *entry)
*/
void
defun(char *name, union node *func)
defun(char *name, union node *func, int lineno)
{
struct cmdentry entry;
INTOFF;
entry.cmdtype = CMDFUNCTION;
entry.lineno = lineno;
entry.lno_frel = fnline1;
entry.u.func = copyfunc(func);
addcmdentry(name, &entry);
INTON;

View File

@ -1,4 +1,4 @@
/* $NetBSD: exec.h,v 1.25 2017/06/04 20:27:14 kre Exp $ */
/* $NetBSD: exec.h,v 1.26 2017/06/07 05:08:32 kre Exp $ */
/*-
* Copyright (c) 1991, 1993
@ -43,7 +43,9 @@
struct cmdentry {
int cmdtype;
short cmdtype;
short lno_frel; /* for functions: Line numbers count from 1 */
int lineno; /* for functions: Abs line number of defn */
union param {
int index;
int (*bltin)(int, char**);
@ -70,6 +72,6 @@ void hashcd(void);
void changepath(const char *);
void deletefuncs(void);
void getcmdentry(char *, struct cmdentry *);
void defun(char *, union node *);
void defun(char *, union node *, int);
int unsetfunc(char *);
void hash_special_builtins(void);

View File

@ -1,4 +1,4 @@
/* $NetBSD: expand.c,v 1.112 2017/06/05 02:15:55 kre Exp $ */
/* $NetBSD: expand.c,v 1.113 2017/06/07 05:08:32 kre Exp $ */
/*-
* Copyright (c) 1991, 1993
@ -37,7 +37,7 @@
#if 0
static char sccsid[] = "@(#)expand.c 8.5 (Berkeley) 5/15/95";
#else
__RCSID("$NetBSD: expand.c,v 1.112 2017/06/05 02:15:55 kre Exp $");
__RCSID("$NetBSD: expand.c,v 1.113 2017/06/07 05:08:32 kre Exp $");
#endif
#endif /* not lint */
@ -119,6 +119,7 @@ STATIC int patmatch(const char *, const char *, int);
STATIC char *cvtnum(int, char *);
static int collate_range_cmp(wchar_t, wchar_t);
STATIC void add_args(struct strlist *);
STATIC void rmescapes_nl(char *);
/*
* Expand shell variables and backquotes inside a here document.
@ -168,6 +169,7 @@ expandarg(union node *arg, struct arglist *arglist, int flag)
ifsfirst.next = NULL;
ifslastp = NULL;
argstr(arg->narg.text, flag);
line_number = arg->narg.lineno;
if (arglist == NULL) {
STACKSTRNUL(expdest);
CTRACE(DBG_EXPAND, ("expandarg: no arglist, done (%d) \"%s\"\n",
@ -257,6 +259,11 @@ argstr(const char *p, int flag)
STPUTC(c, expdest);
ifs_split = 0;
break;
case CTLNONL:
if (flag & EXP_NL)
STPUTC(c, expdest);
line_number++;
break;
case CTLQUOTEEND:
ifs_split = EXP_IFS_SPLIT;
break;
@ -304,6 +311,8 @@ argstr(const char *p, int flag)
}
break;
default:
if (c == '\n')
line_number++;
STPUTC(c, expdest);
if (flag & ifs_split && strchr(ifs, c) != NULL) {
/* We need to get the output split here... */
@ -336,6 +345,8 @@ exptilde(const char *p, int flag)
case CTLENDARI:
case CTLQUOTEMARK:
return (startp);
case CTLNONL:
break;
case ':':
if (flag & EXP_VARTILDE)
goto done;
@ -467,17 +478,18 @@ expari(const char *p, int flag)
*/
quoted = *p++ == '"';
begoff = expdest - stackblock();
VTRACE(DBG_EXPAND, ("expari: '%c' \"%s\" begoff %d\n", p[-1],p,begoff));
p = argstr(p, 0); /* expand $(( )) string */
VTRACE(DBG_EXPAND, ("expari: '%c' \"%s\" begoff %d quotes %x\n",
p[-1],p,begoff, quotes));
p = argstr(p, EXP_NL); /* expand $(( )) string */
STPUTC('\0', expdest);
start = stackblock() + begoff;
VTRACE(DBG_EXPAND, ("expari: argstr added: '%s' start: \"%.8s\"\n",
ed, start));
removerecordregions(begoff);
if (quotes)
rmescapes(start); /* should be a no-op */
rmescapes_nl(start); /* convert CRTNONL back into \n's */
q = grabstackstr(expdest);
result = arith(start);
result = arith(start, line_number);
VTRACE(DBG_EXPAND, ("expari: after arith: result=%jd '%s' q@'%.3s'\n",
result, ed, q));
ungrabstackstr(q, expdest);
@ -782,9 +794,14 @@ evalvar(const char *p, int flag)
again: /* jump here after setting a variable with ${var=text} */
if (varflags & VSLINENO) {
set = 1;
special = p - var;
val = NULL;
if (line_num.flags & VUNSET) {
set = 0;
val = NULL;
} else {
set = 1;
special = p - var;
val = NULL;
}
} else if (special) {
set = varisset(var, varflags & VSNUL);
val = NULL;
@ -951,6 +968,8 @@ evalvar(const char *p, int flag)
for (;;) {
if ((c = *p++) == CTLESC)
p++;
else if (c == CTLNONL)
;
else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
if (set)
argbackq = argbackq->next;
@ -1181,6 +1200,10 @@ ifsbreakup(char *string, struct arglist *arglist)
while (p < string + ifsp->endoff) {
had_param_ch = 1;
q = p;
if (*p == CTLNONL) {
p++;
continue;
}
if (*p == CTLESC)
p++;
if (ifsp->inquotes) {
@ -1220,6 +1243,8 @@ ifsbreakup(char *string, struct arglist *arglist)
/* Ignore further trailing IFS whitespace */
for (; p < string + ifsp->endoff; p++) {
q = p;
if (*p == CTLNONL)
continue;
if (*p == CTLESC)
p++;
if (strchr(ifs, *p) == NULL) {
@ -1366,7 +1391,7 @@ expmeta(char *enddir, char *name)
if (*q == '!')
q++;
for (;;) {
while (*q == CTLQUOTEMARK)
while (*q == CTLQUOTEMARK || *q == CTLNONL)
q++;
if (*q == CTLESC)
q++;
@ -1381,7 +1406,7 @@ expmeta(char *enddir, char *name)
metaflag = 1;
} else if (*p == '\0')
break;
else if (*p == CTLQUOTEMARK)
else if (*p == CTLQUOTEMARK || *p == CTLNONL)
continue;
else if (*p == CTLESC)
p++;
@ -1395,7 +1420,7 @@ expmeta(char *enddir, char *name)
if (enddir != expdir)
metaflag++;
for (p = name ; ; p++) {
if (*p == CTLQUOTEMARK)
if (*p == CTLQUOTEMARK || *p == CTLNONL)
continue;
if (*p == CTLESC)
p++;
@ -1411,7 +1436,7 @@ expmeta(char *enddir, char *name)
if (start != name) {
p = name;
while (p < start) {
while (*p == CTLQUOTEMARK)
while (*p == CTLQUOTEMARK || *p == CTLNONL)
p++;
if (*p == CTLESC)
p++;
@ -1438,7 +1463,7 @@ expmeta(char *enddir, char *name)
}
matchdot = 0;
p = start;
while (*p == CTLQUOTEMARK)
while (*p == CTLQUOTEMARK || *p == CTLNONL)
p++;
if (*p == CTLESC)
p++;
@ -1606,6 +1631,7 @@ patmatch(const char *pattern, const char *string, int squoted)
goto backtrack;
break;
case CTLQUOTEMARK:
case CTLNONL:
continue;
case '?':
if (squoted && *q == CTLESC)
@ -1617,7 +1643,7 @@ patmatch(const char *pattern, const char *string, int squoted)
c = *p;
while (c == CTLQUOTEMARK || c == '*')
c = *++p;
if (c != CTLESC && c != CTLQUOTEMARK &&
if (c != CTLESC && c != CTLQUOTEMARK && c != CTLNONL &&
c != '?' && c != '*' && c != '[') {
while (*q != c) {
if (squoted && *q == CTLESC &&
@ -1648,7 +1674,7 @@ patmatch(const char *pattern, const char *string, int squoted)
if (*endp == '!')
endp++;
for (;;) {
while (*endp == CTLQUOTEMARK)
while (*endp == CTLQUOTEMARK || *endp==CTLNONL)
endp++;
if (*endp == '\0')
goto dft; /* no matching ] */
@ -1670,7 +1696,7 @@ patmatch(const char *pattern, const char *string, int squoted)
chr = (unsigned char)*q++;
c = *p++;
do {
if (c == CTLQUOTEMARK)
if (c == CTLQUOTEMARK || c == CTLNONL)
continue;
if (c == '\0') {
p = savep, q = saveq;
@ -1729,7 +1755,7 @@ backtrack:
/*
* Remove any CTLESC characters from a string.
* Remove any CTLESC or CTLNONL characters from a string.
*/
void
@ -1738,13 +1764,13 @@ rmescapes(char *str)
char *p, *q;
p = str;
while (*p != CTLESC && *p != CTLQUOTEMARK) {
while (*p != CTLESC && *p != CTLQUOTEMARK && *p != CTLNONL) {
if (*p++ == '\0')
return;
}
q = p;
while (*p) {
if (*p == CTLQUOTEMARK) {
if (*p == CTLQUOTEMARK || *p == CTLNONL) {
p++;
continue;
}
@ -1755,6 +1781,58 @@ rmescapes(char *str)
*q = '\0';
}
/*
* and a special version for dealing with expressions to be parsed
* by the arithmetic evaluator. That needs to be able to count \n's
* even ones that were \newline elided \n's, so we have to put the
* latter back into the string - just being careful to put them only
* at a place where white space can reasonably occur in the string
* -- then the \n we insert will just be white space, and ignored
* for all purposes except line counting.
*/
void
rmescapes_nl(char *str)
{
char *p, *q;
int nls = 0, holdnl = 0, holdlast;
p = str;
while (*p != CTLESC && *p != CTLQUOTEMARK && *p != CTLNONL) {
if (*p++ == '\0')
return;
}
if (p > str) /* must reprocess char before stopper (if any) */
--p; /* so we do not place a \n badly */
q = p;
while (*p) {
if (*p == CTLQUOTEMARK) {
p++;
continue;
}
if (*p == CTLNONL) {
p++;
nls++;
continue;
}
if (*p == CTLESC)
p++;
holdlast = holdnl;
holdnl = is_in_name(*p); /* letters, digits, _ */
if (q == str || is_space(q[-1]) || (*p != '=' && q[-1] != *p)) {
if (nls > 0 && holdnl != holdlast) {
while (nls > 0)
*q++ = '\n', nls--;
}
}
*q++ = *p++;
}
while (--nls >= 0)
*q++ = '\n';
*q = '\0';
}
/*

View File

@ -1,4 +1,4 @@
/* $NetBSD: expand.h,v 1.22 2017/06/03 10:31:16 kre Exp $ */
/* $NetBSD: expand.h,v 1.23 2017/06/07 05:08:32 kre Exp $ */
/*-
* Copyright (c) 1991, 1993
@ -58,6 +58,7 @@ struct arglist {
#define EXP_IFS_SPLIT 0x20 /* need to record arguments for ifs breakup */
#define EXP_IN_QUOTES 0x40 /* don't set EXP_IFS_SPLIT again */
#define EXP_GLOB 0x80 /* perform filename globbing */
#define EXP_NL 0x100 /* keep CRTNONL in output */
#define EXP_FULL (EXP_SPLIT | EXP_GLOB)

View File

@ -1,4 +1,4 @@
/* $NetBSD: input.c,v 1.57 2017/06/07 04:44:17 kre Exp $ */
/* $NetBSD: input.c,v 1.58 2017/06/07 05:08:32 kre Exp $ */
/*-
* Copyright (c) 1991, 1993
@ -37,7 +37,7 @@
#if 0
static char sccsid[] = "@(#)input.c 8.3 (Berkeley) 6/9/95";
#else
__RCSID("$NetBSD: input.c,v 1.57 2017/06/07 04:44:17 kre Exp $");
__RCSID("$NetBSD: input.c,v 1.58 2017/06/07 05:08:32 kre Exp $");
#endif
#endif /* not lint */
@ -65,6 +65,7 @@ __RCSID("$NetBSD: input.c,v 1.57 2017/06/07 04:44:17 kre Exp $");
#include "alias.h"
#include "parser.h"
#include "myhistedit.h"
#include "show.h"
#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
@ -482,12 +483,14 @@ setinputstring(char *string, int push, int line1)
{
INTOFF;
if (push)
if (push) /* XXX: always, as it happens */
pushfile();
parsenextc = string;
parselleft = parsenleft = strlen(string);
parsefile->buf = NULL;
plinno = line1;
TRACE(("setinputstring(\"%.20s%s\" (%d), %d, %d)\n", string,
(parsenleft > 20 ? "..." : ""), parsenleft, push, line1));
INTON;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: jobs.c,v 1.85 2017/05/18 13:34:17 kre Exp $ */
/* $NetBSD: jobs.c,v 1.86 2017/06/07 05:08:32 kre Exp $ */
/*-
* Copyright (c) 1991, 1993
@ -37,7 +37,7 @@
#if 0
static char sccsid[] = "@(#)jobs.c 8.5 (Berkeley) 5/4/95";
#else
__RCSID("$NetBSD: jobs.c,v 1.85 2017/05/18 13:34:17 kre Exp $");
__RCSID("$NetBSD: jobs.c,v 1.86 2017/06/07 05:08:32 kre Exp $");
#endif
#endif /* not lint */
@ -1431,22 +1431,25 @@ cmdputs(const char *s)
nleft = cmdnleft;
while (nleft > 0 && (c = *p++) != 0) {
switch (c) {
case CTLNONL:
c = '\0';
break;
case CTLESC:
c = *p++;
break;
case CTLVAR:
subtype = *p++;
if (subtype & VSLINENO) {
if (subtype & VSLINENO) { /* undo LINENO hack */
if ((subtype & VSTYPE) == VSLENGTH)
str = "${#LINENO";
str = "${#LINENO"; /*}*/
else
str = "${LINENO";
str = "${LINENO"; /*}*/
while (is_digit(*p))
p++;
} else if ((subtype & VSTYPE) == VSLENGTH)
str = "${#";
str = "${#"; /*}*/
else
str = "${";
str = "${"; /*}*/
if (!(subtype & VSQUOTE) != !(quoted & 1)) {
quoted ^= 1;
c = '"';
@ -1454,7 +1457,7 @@ cmdputs(const char *s)
c = *str++;
}
break;
case CTLENDVAR:
case CTLENDVAR: /*{*/
c = '}';
if (quoted & 1)
str = "\"";
@ -1471,9 +1474,11 @@ cmdputs(const char *s)
break;
case CTLARI:
c = '$';
str = "((";
if (*p == ' ')
p++;
str = "(("; /*))*/
break;
case CTLENDARI:
case CTLENDARI: /*((*/
c = ')';
str = ")";
break;
@ -1492,7 +1497,7 @@ cmdputs(const char *s)
if (subtype & VSNUL)
c = ':';
else
c = *str++;
c = *str++; /*{*/
if (c != '}')
quoted <<= 1;
else if (*p == CTLENDVAR)

View File

@ -1,4 +1,4 @@
# $NetBSD: nodetypes,v 1.16 2017/06/07 04:44:17 kre Exp $
# $NetBSD: nodetypes,v 1.17 2017/06/07 05:08:32 kre Exp $
# Copyright (c) 1991, 1993
# The Regents of the University of California. All rights reserved.
#
@ -104,6 +104,7 @@ NCLIST nclist # a case
next nodeptr # the next case in list
pattern nodeptr # list of patterns for this case
body nodeptr # code to execute for this case
lineno int
NDEFUN narg # define a function. The "next" field contains
@ -114,6 +115,7 @@ NARG narg # represents a word
next nodeptr # next word in list
text string # the text of the word
backquote nodelist # list of commands in back quotes
lineno int
NTO nfile # fd> fname
NCLOBBER nfile # fd>| fname

View File

@ -1,4 +1,4 @@
/* $NetBSD: option.list,v 1.2 2017/05/28 14:14:22 kre Exp $ */
/* $NetBSD: option.list,v 1.3 2017/06/07 05:08:32 kre Exp $ */
/*
* define the shell's settable options
@ -63,6 +63,7 @@ usefork fork F # use fork(2) instead of vfork(2)
pflag nopriv p # preserve privs if set[ug]id
posix posix # be closer to POSIX compat
qflag quietprofile q # disable -v/-x in startup files
fnline1 lineno_fn_relative L on # number lines in funcs starting at 1
// editline/history related options ("vi" is standard, 'V' and others are not)
// only one of vi/emacs can be set, hence the "set" definition, value

View File

@ -1,4 +1,4 @@
/* $NetBSD: parser.c,v 1.133 2017/06/07 04:44:17 kre Exp $ */
/* $NetBSD: parser.c,v 1.134 2017/06/07 05:08:32 kre Exp $ */
/*-
* Copyright (c) 1991, 1993
@ -37,7 +37,7 @@
#if 0
static char sccsid[] = "@(#)parser.c 8.7 (Berkeley) 5/16/95";
#else
__RCSID("$NetBSD: parser.c,v 1.133 2017/06/07 04:44:17 kre Exp $");
__RCSID("$NetBSD: parser.c,v 1.134 2017/06/07 05:08:32 kre Exp $");
#endif
#endif /* not lint */
@ -100,6 +100,7 @@ struct heredoc *heredoc;
int quoteflag; /* set if (part of) last token was quoted */
int startlinno; /* line # where last token started */
int funclinno; /* line # where the current function started */
int elided_nl; /* count of \ \n (deleted \n's) we have seen */
STATIC union node *list(int, int);
@ -109,7 +110,7 @@ STATIC union node *command(void);
STATIC union node *simplecmd(union node **, union node *);
STATIC union node *makename(void);
STATIC void parsefname(void);
STATIC void slurp_heredoc(char *const, const int, const int);
STATIC int slurp_heredoc(char *const, const int, const int);
STATIC void readheredocs(void);
STATIC int peektoken(void);
STATIC int readtoken(void);
@ -124,6 +125,9 @@ STATIC int pgetc_linecont(void);
static const char EOFhere[] = "EOF reading here (<<) document";
#ifdef DEBUG
int parsing = 0;
#endif
/*
* Read and parse a command. Returns NEOF on end of file. (NULL is a
@ -136,6 +140,9 @@ parsecmd(int interact)
int t;
union node *n;
#ifdef DEBUG
parsing++;
#endif
tokpushback = 0;
doprompt = interact;
if (doprompt)
@ -144,12 +151,22 @@ parsecmd(int interact)
setprompt(0);
needprompt = 0;
t = readtoken();
#ifdef DEBUG
parsing--;
#endif
if (t == TEOF)
return NEOF;
if (t == TNL)
return NULL;
#ifdef DEBUG
parsing++;
#endif
tokpushback++;
n = list(1, 0);
#ifdef DEBUG
parsing--;
#endif
if (heredoclist)
error("%d: Here document (<<%s) expected but not present",
heredoclist->startline, heredoclist->eofmark);
@ -397,6 +414,7 @@ command(void)
n2->type = NARG;
n2->narg.text = wordtext;
n2->narg.backquote = backquotelist;
n2->narg.lineno = startlinno - elided_nl;
*app = n2;
app = &n2->narg.next;
}
@ -414,6 +432,7 @@ command(void)
n2->narg.text = argvars;
n2->narg.backquote = NULL;
n2->narg.next = NULL;
n2->narg.lineno = startlinno - elided_nl;
n1->nfor.args = n2;
/*
* Newline or semicolon here is optional (but note
@ -437,13 +456,14 @@ command(void)
case TCASE:
n1 = stalloc(sizeof(struct ncase));
n1->type = NCASE;
n1->ncase.lineno = startlinno;
n1->ncase.lineno = startlinno - elided_nl;
if (readtoken() != TWORD)
synexpect(TWORD, 0);
n1->ncase.expr = n2 = stalloc(sizeof(struct narg));
n2->type = NARG;
n2->narg.text = wordtext;
n2->narg.backquote = backquotelist;
n2->narg.lineno = startlinno - elided_nl;
n2->narg.next = NULL;
while (readtoken() == TNL);
if (lasttoken != TWORD || ! equal(wordtext, "in"))
@ -470,6 +490,7 @@ command(void)
for (;;) {
*app = ap = stalloc(sizeof(struct narg));
ap->type = NARG;
ap->narg.lineno = startlinno - elided_nl;
ap->narg.text = wordtext;
ap->narg.backquote = backquotelist;
if (checkkwd = 2, readtoken() != TPIPE)
@ -482,6 +503,7 @@ command(void)
if (lasttoken != TRP) {
synexpect(TRP, 0);
}
cp->nclist.lineno = startlinno - elided_nl;
cp->nclist.body = list(0, 0);
checkkwd = 2;
@ -601,6 +623,7 @@ simplecmd(union node **rpp, union node *redir)
{
union node *args, **app;
union node *n = NULL;
int line = 0;
#ifdef BOGUS_NOT_COMMAND
union node *n2;
int negate = 0;
@ -627,13 +650,18 @@ simplecmd(union node **rpp, union node *redir)
for (;;) {
if (readtoken() == TWORD) {
if (line == 0)
line = startlinno;
n = stalloc(sizeof(struct narg));
n->type = NARG;
n->narg.text = wordtext;
n->narg.backquote = backquotelist;
n->narg.lineno = startlinno - elided_nl;
*app = n;
app = &n->narg.next;
} else if (lasttoken == TREDIR) {
if (line == 0)
line = startlinno;
*rpp = n = redirnode;
rpp = &n->nfile.next;
parsefname(); /* read name of redirection file */
@ -646,7 +674,10 @@ simplecmd(union node **rpp, union node *redir)
rmescapes(n->narg.text);
if (strchr(n->narg.text, '/'))
synerror("Bad function name");
VTRACE(DBG_PARSE, ("Function '%s' seen @%d\n",
n->narg.text, plinno));
n->type = NDEFUN;
n->narg.lineno = plinno - elided_nl;
n->narg.next = command();
funclinno = 0;
goto checkneg;
@ -662,6 +693,7 @@ simplecmd(union node **rpp, union node *redir)
*rpp = NULL;
n = stalloc(sizeof(struct ncmd));
n->type = NCMD;
n->ncmd.lineno = line - elided_nl;
n->ncmd.backgnd = 0;
n->ncmd.args = args;
n->ncmd.redirect = redir;
@ -692,6 +724,7 @@ makename(void)
n->narg.next = NULL;
n->narg.text = wordtext;
n->narg.backquote = backquotelist;
n->narg.lineno = startlinno - elided_nl;
return n;
}
@ -816,11 +849,12 @@ checkend(int c, char * const eofmark, const int striptabs)
* Input any here documents.
*/
STATIC void
STATIC int
slurp_heredoc(char *const eofmark, const int striptabs, const int sq)
{
int c;
char *out;
int lines = plinno;
c = pgetc();
@ -861,6 +895,7 @@ slurp_heredoc(char *const eofmark, const int striptabs, const int sq)
if (c == '\\') { /* A backslash */
c = pgetc(); /* followed by */
if (c == '\n') { /* a newline? */
/*XXX CTLNONL ?? XXX*/
plinno++;
continue; /* :drop both */
}
@ -889,9 +924,22 @@ slurp_heredoc(char *const eofmark, const int striptabs, const int sq)
wordtext = out;
VTRACE(DBG_PARSE,
("Slurped a heredoc (to '%s')%s: len %d, \"%.*s%s\" @%d\n",
eofmark, striptabs ? " tab stripped" : "", c, (c > 16 ? 16 : c),
("Slurped a %d line %sheredoc (to '%s')%s: len %d, \"%.*s%s\" @%d\n",
plinno - lines, sq ? "quoted " : "", eofmark,
striptabs ? " tab stripped" : "", c, (c > 16 ? 16 : c),
wordtext, (c > 16 ? "..." : ""), plinno));
return (plinno - lines);
}
static char *
insert_elided_nl(char *str)
{
while (elided_nl > 0) {
STPUTC(CTLNONL, str);
elided_nl--;
}
return str;
}
STATIC void
@ -899,8 +947,14 @@ readheredocs(void)
{
struct heredoc *here;
union node *n;
int line, l;
line = 0; /*XXX - gcc! obviously unneeded */
if (heredoclist)
line = heredoclist->startline + 1;
l = 0;
while (heredoclist) {
line += l;
here = heredoclist;
heredoclist = here->next;
if (needprompt) {
@ -908,7 +962,7 @@ readheredocs(void)
needprompt = 0;
}
slurp_heredoc(here->eofmark, here->striptabs,
l = slurp_heredoc(here->eofmark, here->striptabs,
here->here->nhere.type == NHERE);
n = stalloc(sizeof(struct narg));
@ -924,7 +978,7 @@ readheredocs(void)
/*
* Now "parse" here docs that have unquoted eofmarkers.
*/
setinputstring(wordtext, 1, startlinno);
setinputstring(wordtext, 1, line);
readtoken1(pgetc(), DQSYNTAX, 1);
n->narg.text = wordtext;
n->narg.backquote = backquotelist;
@ -978,8 +1032,8 @@ readtoken(void)
lasttoken = t = pp -
parsekwd + KWDOFFSET;
VTRACE(DBG_PARSE,
("keyword %s recognized\n",
tokname[t]));
("keyword %s recognized @%d\n",
tokname[t], plinno));
goto out;
}
}
@ -993,8 +1047,8 @@ readtoken(void)
out:
checkkwd = (t == TNOT) ? savecheckkwd : 0;
}
VTRACE(DBG_PARSE, ("%stoken %s %s\n", alreadyseen ? "reread " : "",
tokname[t], t == TWORD ? wordtext : ""));
VTRACE(DBG_PARSE, ("%stoken %s %s @%d\n", alreadyseen ? "reread " : "",
tokname[t], t == TWORD ? wordtext : "", plinno));
return (t);
}
@ -1032,6 +1086,7 @@ xxreadtoken(void)
setprompt(2);
needprompt = 0;
}
elided_nl = 0;
startlinno = plinno;
for (;;) { /* until token or start of word found */
c = pgetc_macro();
@ -1315,12 +1370,15 @@ parsebackq(VSS *const stack, char * const in,
handler = &jmploc;
INTON;
if (oldstyle) {
/* We must read until the closing backquote, giving special
treatment to some slashes, and then push the string and
reread it as input, interpreting it normally. */
/*
* We must read until the closing backquote, giving special
* treatment to some slashes, and then push the string and
* reread it as input, interpreting it normally.
*/
int pc;
int psavelen;
char *pstr;
int line1 = plinno;
/*
* Because the entire `...` is read here, we don't
@ -1333,25 +1391,12 @@ parsebackq(VSS *const stack, char * const in,
setprompt(2);
needprompt = 0;
}
switch (pc = pgetc_linecont()) {
case '`':
goto done;
pc = pgetc_linecont();
if (pc == '`')
break;
switch (pc) {
case '\\':
if ((pc = pgetc()) == '\n') {
plinno++;
if (doprompt)
setprompt(2);
else
setprompt(0);
/*
* If eating a newline, avoid putting
* the newline into the new character
* stream (via the STPUTC after the
* switch).
*/
continue;
}
pc = pgetc(); /* cannot be '\n' */
if (pc != '\\' && pc != '`' && pc != '$'
&& (!ISDBLQUOTE() || pc != '"'))
STPUTC('\\', out);
@ -1359,6 +1404,7 @@ parsebackq(VSS *const stack, char * const in,
case '\n':
plinno++;
out = insert_elided_nl(out);
needprompt = doprompt;
break;
@ -1372,12 +1418,11 @@ parsebackq(VSS *const stack, char * const in,
}
STPUTC(pc, out);
}
done:
STPUTC('\0', out);
psavelen = out - stackblock();
if (psavelen > 0) {
pstr = grabstackstr(out);
setinputstring(pstr, 1, startlinno);
setinputstring(pstr, 1, line1);
}
}
nlpp = pbqlist;
@ -1549,10 +1594,13 @@ readtoken1(int firstc, char const *syn, int magicq)
bqlist = NULL;
arinest = 0;
parenlevel = 0;
elided_nl = 0;
STARTSTACKSTR(out);
for (c = firstc ;; c = pgetc_macro()) { /* until of token */
if (syntax == ARISYNTAX)
out = insert_elided_nl(out);
CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
switch (syntax[c]) {
case CNL: /* '\n' */
@ -1583,6 +1631,7 @@ readtoken1(int firstc, char const *syn, int magicq)
}
if (c == '\n') {
plinno++;
elided_nl++;
if (doprompt)
setprompt(2);
else
@ -1656,6 +1705,7 @@ readtoken1(int firstc, char const *syn, int magicq)
}
continue;
case CVAR: /* '$' */
out = insert_elided_nl(out);
PARSESUB(); /* parse substitution */
continue;
case CENDVAR: /* CLOSEBRACE */
@ -1665,6 +1715,7 @@ readtoken1(int firstc, char const *syn, int magicq)
} else {
USTPUTC(c, out);
}
out = insert_elided_nl(out);
continue;
case CLP: /* '(' in arithmetic */
parenlevel++;
@ -1676,6 +1727,7 @@ readtoken1(int firstc, char const *syn, int magicq)
--parenlevel;
} else {
if (pgetc_linecont() == ')') {
out = insert_elided_nl(out);
if (--arinest == 0) {
TS_POP();
USTPUTC(CTLENDARI, out);
@ -1750,23 +1802,22 @@ readtoken1(int firstc, char const *syn, int magicq)
*/
parsesub: {
char buf[10];
int subtype;
int typeloc;
int flags;
char *p;
static const char types[] = "}-+?=";
int i;
int linno;
c = pgetc_linecont();
if (c != '(' && c != OPENBRACE && !is_name(c) && !is_special(c)) {
if (c != '('/*)*/ && c != OPENBRACE && !is_name(c) && !is_special(c)) {
USTPUTC('$', out);
pungetc();
} else if (c == '(') { /* $(command) or $((arith)) */
if (pgetc_linecont() == '(') {
} else if (c == '('/*)*/) { /* $(command) or $((arith)) */
if (pgetc_linecont() == '(' /*')'*/ ) {
out = insert_elided_nl(out);
PARSEARITH();
} else {
out = insert_elided_nl(out);
pungetc();
out = parsebackq(stack, out, &bqlist, 0);
}
@ -1822,9 +1873,18 @@ parsesub: {
STPUTC(c, out);
c = pgetc_linecont();
} while (is_in_name(c));
#if 0
if (out - p == 6 && strncmp(p, "LINENO", 6) == 0) {
/* Replace the variable name with the
* current line number. */
int i;
int linno;
char buf[10];
/*
* The "LINENO hack"
*
* Replace the variable name with the
* current line number.
*/
linno = plinno;
if (funclinno != 0)
linno -= funclinno - 1;
@ -1834,9 +1894,10 @@ parsesub: {
STPUTC(buf[i], out);
flags |= VSLINENO;
}
#endif
} else if (is_digit(c)) {
do {
USTPUTC(c, out);
STPUTC(c, out);
c = pgetc_linecont();
} while (subtype != VSNORMAL && is_digit(c));
}
@ -1911,6 +1972,8 @@ parsearith: {
/*
* we collapse embedded arithmetic expansion to
* parentheses, which should be equivalent
*
* XXX It isn't, must fix, soonish...
*/
USTPUTC('(', out);
USTPUTC('(', out);
@ -1940,6 +2003,7 @@ parsearith: {
#ifdef mkinit
RESET {
tokpushback = 0;
@ -2058,6 +2122,7 @@ pgetc_linecont(void)
c = pgetc();
if (c == '\n') {
plinno++;
elided_nl++;
if (doprompt)
setprompt(2);
else

View File

@ -1,4 +1,4 @@
/* $NetBSD: parser.h,v 1.21 2016/03/31 23:11:05 christos Exp $ */
/* $NetBSD: parser.h,v 1.22 2017/06/07 05:08:32 kre Exp $ */
/*-
* Copyright (c) 1991, 1993
@ -46,7 +46,9 @@
#define CTLENDARI '\207'
#define CTLQUOTEMARK '\210'
#define CTLQUOTEEND '\211' /* only inside ${...} */
#define CTL_LAST '\211' /* last 'special' character */
#define CTLNONL '\212' /* The \n in a deleted \ \n sequence */
/* pure concidence that (CTLNONL & 0x7f) == '\n' */
#define CTL_LAST '\212' /* last 'special' character */
/* variable substitution byte (follows CTLVAR) */
#define VSTYPE 0x0f /* type of variable substitution */
@ -83,3 +85,7 @@ union node *parsecmd(int);
void fixredir(union node *, const char *, int);
int goodname(char *);
const char *getprompt(void *);
#ifdef DEBUG
extern int parsing;
#endif

View File

@ -1,4 +1,4 @@
.\" $NetBSD: sh.1,v 1.148 2017/06/06 22:38:52 kre Exp $
.\" $NetBSD: sh.1,v 1.149 2017/06/07 05:08:32 kre Exp $
.\" Copyright (c) 1991, 1993
.\" The Regents of the University of California. All rights reserved.
.\"
@ -294,19 +294,23 @@ the file system is searched for commands each time the function is invoked.
Force the shell to behave interactively.
.It Fl I Em ignoreeof
Ignore EOFs from input when interactive.
.\" .It Fl L Em lineno_fn_relative
.\" When set, before a function is defined,
.\" causes the variable
.\" .Ev LINENO
.\" when used within the function,
.\" to refer to the line number defined such that
.\" first line of the function is line 1.
.\" When reset,
.\" .Ev LINENO
.\" in a function refers to the line number within the file
.\" in which the definition of the function occurs
.\" (which can be the number of lines of shell input from standard input
.\" when a function is defined interactively from the command prompt.)
.It Fl L Em lineno_fn_relative
When set, before a function is defined,
causes the variable
.Ev LINENO
when used within the function,
to refer to the line number defined such that
first line of the function is line 1.
When reset,
.Ev LINENO
in a function refers to the line number within the file
in which the definition of the function occurs.
This option defaults to
.Dq on
in this shell.
For more details see the section
.Sx LINENO
below.
.It Fl m Em monitor
Turn on job control (set automatically when interactive).
.It Fl n Em noexec
@ -2228,6 +2232,12 @@ differs amongst shell implementations.
Using
.Dq local \&\-
is an extension not implemented by most shells.
.Pp
See the section
.Sx LINENO
below for details of the effects of making the variable
.Ev LINENO
local.
.It pwd Op Fl \&LP
Print the current directory.
If
@ -2765,8 +2775,9 @@ See
.Xr nls 7 .
.It Ev LINENO
The current line number in the script or function.
The value of this variable is automatically set by the
shell, even if made readonly.
See the section
.Sx LINENO
below for more details.
.It Ev MAIL
The name of a mail file, that will be checked for the arrival of new mail.
Overridden by
@ -2840,6 +2851,153 @@ that were used when building the shell will be listed.
behaves like any other variable that has the read-only
and un-exportable attributes set.
.El
.Ss Ev LINENO
.Ev LINENO
is in many respects a normal shell variable, containing an
integer value. and can be expanded using any of the forms
mentioned above which can be used for any other variable.
.Pp
.Ev LINENO
can be exported,
made readonly (which prevents attempts to assign to it,
and to unset it, but which does not change the value,
that is the current line number, from being obtained when
.Ev LINENO
is referenced,)
and can be unset.
All of those act as they would with any other variable.
However,
.Ev LINENO
should normally not ever be set, in this
shell doing so reverses the effect of an earlier unset,
but does not otherwise affect the value obtained.
If unset,
.Ev LINENO
should not be set again.
If
.Ev LINENO
is set or unset, different shells act differently.
The value of
.Ev LINENO
is never imported from the environment when the shell is
started, though if present there, as with any other variable,
.Ev LINENO
will be exported by this shell.
.Pp
.Ev LINENO
is set automatically by the shell to be the number of the source
line on which it occurs.
When exported,
.Ev LINENO
is exported with its value set to the line number it would have
had had it been referenced on the command line of the command to
which it is exported.
Line numbers are counted from 1, which is the first line the shell
reads from any particular file.
For this shell, standard input, including in an interactive shell,
the user's terminal, is just another file and lines are counted
there as well, however note that not all shells count interactive
lines this way, it is wise to rely upon LINENO only having a useful
value in a script, or a function.
.Pp
The role of
.Ev LINENO
in functions is less clear.
In some shells,
.Ev LINENO
continues to refer to the line number in the script which defines
the function,
in others lines count from one within the function, always (and
resume counting normally once the function definition is complete)
and others count in functions from one if the function is defined
interactively, but otherwise just reference the line number in the
script in which the function is defined.
This shell gives the user the option to choose.
If the
.Fl L
flag (the
.Ic lineno_fn_relative
option) is set, when the function is defined, then the function
defaults to counting lines with one being the first line of the
function.
When the
.Fl L
flag is not set, the shell counts lines in a function definition
in the same continuous sequence as the lines that surround the
function definition. Further, if
.Ev LINENO
is made local
(see
.Sx Built-ins
above)
inside the function, the function can decide which
behavior it prefers.
If
.Ev LINENO
is made local and inherited, and not given a value, as in
.Dl local -I LINENO
then from that point in the function,
.Ev LINENO
will give the line number as if lines are counted in sequence
with the lines that surround the function definition.
If
.Ev LINENO
is made local, and in that same command, given a value, as
.Dl local Oo Fl I Ns Cm \&| Ns Fl N Ns Oc LINENO=value
then
.Ev LINENO
will give the line number as if lines are counted from one
from the beginning of the function.
The value nominally assigned in this case is irrelevant, and ignored.
For completeness, if lineno is made local and unset, as in
.Dl local -N LINENO
then
.Ev LINENO
is simply unset inside the function, and gives no value at all.
.Pp
Now for some technical details.
The line on which
.Ev LINENO
occurs in a parameter expansion, is the line that contains the
.Sq \&$
that begins the expansion of
.Ev LINENO .
In the case of nested expansions, that
.Sq \&$
is the one that actually has
.Ev LINENO
as its parameter.
In an arithmetic expansion, where no
.Sq \&$
is used to evaluate
.Ev LINENO
but
.Ev LINENO
is simply referenced as a variable, then the value is the
line number of the line that contains the
.Sq L
of
.Ev LINENO .
For functions line one of the function definition (when relevant)
is the line that contains the first character of the
function name in the definition.
When exported, the line number of the command is the line number
where the first character of the word which becomes the command name occurs.
.Pp
When the shell opens a new file, for any reason,
it counts lines from one in that file,
and then resumes its original counting once it resumes reading the
previous input stream.
When handling a string passed to
.Ic eval
the line number starts at the line on which the string starts,
and then if the string contains internal newline characters,
those characters increase the line number.
This means that references to
.Ev LINENO
in such a case can produce values larger than would be
produced by a reference on the line after the
.Ic eval .
.Sh FILES
.Bl -item
.It

View File

@ -1,4 +1,4 @@
/* $NetBSD: show.c,v 1.42 2017/05/29 14:03:23 kre Exp $ */
/* $NetBSD: show.c,v 1.43 2017/06/07 05:08:32 kre Exp $ */
/*-
* Copyright (c) 1991, 1993
@ -39,7 +39,7 @@
#if 0
static char sccsid[] = "@(#)show.c 8.3 (Berkeley) 5/4/95";
#else
__RCSID("$NetBSD: show.c,v 1.42 2017/05/29 14:03:23 kre Exp $");
__RCSID("$NetBSD: show.c,v 1.43 2017/06/07 05:08:32 kre Exp $");
#endif
#endif /* not lint */
@ -65,6 +65,7 @@ __RCSID("$NetBSD: show.c,v 1.42 2017/05/29 14:03:23 kre Exp $");
#include "syntax.h"
#include "input.h"
#include "output.h"
#include "var.h"
#include "builtins.h"
#if defined(DEBUG) && !defined(DBG_PID)
@ -654,12 +655,17 @@ sharg(union node *arg, TFILE *fp)
trace_putc(*++p, fp);
break;
case CTLNONL:
trace_putc('\\', fp);
trace_putc('\n', fp);
break;
case CTLVAR:
subtype = *++p;
if (!quoted != !(subtype & VSQUOTE))
trace_putc('"', fp);
trace_putc('$', fp);
trace_putc('{', fp);
trace_putc('{', fp); /*}*/
if ((subtype & VSTYPE) == VSLENGTH)
trace_putc('#', fp);
if (subtype & VSLINENO)
@ -750,6 +756,8 @@ sharg(union node *arg, TFILE *fp)
quoted--;
break;
case CTLARI:
if (*p == ' ')
p++;
trace_puts("$(( ", fp);
break;
case CTLENDARI:
@ -943,13 +951,22 @@ trace_id(TFILE *tf)
} else
indent[0] = '\0';
lno = plinno; /* only approximate for now - as good as we can do */
/*
* If we are in the parser, then plinno is the current line
* number being processed (parser line no).
* If we are elsewhere, then line_number gives the source
* line of whatever we are currently doing (close enough.)
*/
if (parsing)
lno = plinno;
else
lno = line_number;
if (DFlags & DBG_PID) {
i = getpid();
if (DFlags & DBG_LINE)
(void) asprintf(&p, "%5d%c%s\t%4d @\t", i,
i == tf->pid ? ':' : '=', indent, lno);
(void) asprintf(&p, "%5d%c%s\t%4d%c@\t", i,
i == tf->pid ? ':' : '=', indent, lno, parsing?'-':'+');
else
(void) asprintf(&p, "%5d%c%s\t", i,
i == tf->pid ? ':' : '=', indent);

View File

@ -1,7 +1,7 @@
/* $NetBSD: syntax.c,v 1.3 2012/03/28 20:11:25 christos Exp $ */
/* $NetBSD: syntax.c,v 1.4 2017/06/07 05:08:32 kre Exp $ */
#include <sys/cdefs.h>
__RCSID("$NetBSD: syntax.c,v 1.3 2012/03/28 20:11:25 christos Exp $");
__RCSID("$NetBSD: syntax.c,v 1.4 2017/06/07 05:08:32 kre Exp $");
#include <limits.h>
#include "shell.h"
@ -102,4 +102,7 @@ const char is_type[257] = { 0,
set('-', ISSPECL)
set('*', ISSPECL)
set('@', ISSPECL)
set(' ', ISSPACE)
set('\t', ISSPACE)
set('\n', ISSPACE)
};

View File

@ -1,4 +1,4 @@
/* $NetBSD: syntax.h,v 1.7 2017/05/15 20:00:36 kre Exp $ */
/* $NetBSD: syntax.h,v 1.8 2017/06/07 05:08:32 kre Exp $ */
/*-
* Copyright (c) 1991, 1993
@ -56,6 +56,7 @@
#define ISLOWER 04 /* a lower case letter */
#define ISUNDER 010 /* an underscore */
#define ISSPECL 020 /* the name of a special parameter */
#define ISSPACE 040 /* a white space character */
#define PEOF (CHAR_MIN - 1)
#define SYNBASE (-PEOF)
@ -75,6 +76,7 @@
#define is_name(c) (sh_ctype(c) & (ISUPPER|ISLOWER|ISUNDER))
#define is_in_name(c) (sh_ctype(c) & (ISUPPER|ISLOWER|ISUNDER|ISDIGIT))
#define is_special(c) (sh_ctype(c) & (ISSPECL|ISDIGIT))
#define is_space(c) (sh_ctype(c) & ISSPACE)
#define digit_val(c) ((c) - '0')
extern const char basesyntax[];

View File

@ -1,4 +1,4 @@
/* $NetBSD: var.c,v 1.56 2017/06/07 04:44:17 kre Exp $ */
/* $NetBSD: var.c,v 1.57 2017/06/07 05:08:32 kre Exp $ */
/*-
* Copyright (c) 1991, 1993
@ -37,7 +37,7 @@
#if 0
static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 5/4/95";
#else
__RCSID("$NetBSD: var.c,v 1.56 2017/06/07 04:44:17 kre Exp $");
__RCSID("$NetBSD: var.c,v 1.57 2017/06/07 05:08:32 kre Exp $");
#endif
#endif /* not lint */
@ -82,8 +82,12 @@ struct varinit {
struct var *var;
int flags;
const char *text;
void (*func)(const char *);
union var_func_union v_u;
};
#define func v_u.set_func
#define rfunc v_u.ref_func
char *get_lineno(struct var *);
struct localvar *localvars;
@ -102,40 +106,45 @@ struct var vvers;
struct var voptind;
struct var line_num;
struct var line_num;
int line_number;
int funclinebase = 0;
int funclineabs = 0;
char ifs_default[] = " \t\n";
const struct varinit varinit[] = {
#ifndef SMALL
{ &vhistsize, VSTRFIXED|VTEXTFIXED|VUNSET, "HISTSIZE=",
sethistsize },
{ .set_func= sethistsize } },
#endif
{ &vifs, VSTRFIXED|VTEXTFIXED, "IFS= \t\n",
NULL },
{ NULL } },
{ &vmail, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL=",
NULL },
{ NULL } },
{ &vmpath, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH=",
NULL },
{ NULL } },
{ &vvers, VSTRFIXED|VTEXTFIXED|VNOEXPORT, "NETBSD_SHELL=",
NULL },
{ NULL } },
{ &vpath, VSTRFIXED|VTEXTFIXED, "PATH=" _PATH_DEFPATH,
changepath },
{ .set_func= changepath } },
/*
* vps1 depends on uid
*/
{ &vps2, VSTRFIXED|VTEXTFIXED, "PS2=> ",
NULL },
{ NULL } },
{ &vps4, VSTRFIXED|VTEXTFIXED, "PS4=+ ",
NULL },
{ NULL } },
#ifndef SMALL
{ &vterm, VSTRFIXED|VTEXTFIXED|VUNSET, "TERM=",
setterm },
{ .set_func= setterm } },
#endif
{ &voptind, VSTRFIXED|VTEXTFIXED|VNOFUNC, "OPTIND=1",
getoptsreset },
{ &line_num, VSTRFIXED|VTEXTFIXED, "LINENO=1",
NULL },
{ .set_func= getoptsreset } },
{ &line_num, VSTRFIXED|VTEXTFIXED|VFUNCREF, "LINENO=1",
{ .ref_func= get_lineno } },
{ NULL, 0, NULL,
NULL }
{ NULL } }
};
struct var *vartab[VTABSIZE];
@ -170,6 +179,7 @@ INIT {
* PPID is readonly
* Always default IFS
* NETBSD_SHELL is a constant (readonly), and is never exported
* LINENO is simply magic...
*/
snprintf(buf, sizeof(buf), "%d", (int)getppid());
setvar("PPID", buf, VREADONLY);
@ -232,7 +242,7 @@ initvar(void)
*vpp = vp;
vp->text = strdup(ip->text);
vp->flags = ip->flags;
vp->func = ip->func;
vp->v_u = ip->v_u;
}
/*
* PS1 depends on uid
@ -346,9 +356,10 @@ setvareq(char *s, int flags)
error("%.*s: is read only", vp->name_len, s);
if (flags & VNOSET)
return;
INTOFF;
if (vp->func && (flags & VNOFUNC) == 0)
if (vp->func && !(vp->flags & VFUNCREF) && !(flags & VNOFUNC))
(*vp->func)(s + vp->name_len + 1);
if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
@ -368,6 +379,7 @@ setvareq(char *s, int flags)
*/
if (vp == &vmpath || (vp == &vmail && ! mpathset()))
chkmail(1);
INTON;
return;
}
@ -375,7 +387,7 @@ setvareq(char *s, int flags)
if (flags & VNOSET)
return;
vp = ckmalloc(sizeof (*vp));
vp->flags = flags & ~VNOFUNC;
vp->flags = flags & ~(VNOFUNC|VFUNCREF);
vp->text = s;
vp->name_len = nlen;
vp->next = *vpp;
@ -423,6 +435,8 @@ lookupvar(const char *name)
v = find_var(name, NULL, NULL);
if (v == NULL || v->flags & VUNSET)
return NULL;
if (v->rfunc && (v->flags & VFUNCREF) != 0)
return (*v->rfunc)(v) + v->name_len + 1;
return v->text + v->name_len + 1;
}
@ -449,6 +463,8 @@ bltinlookup(const char *name, int doall)
if (v == NULL || v->flags & VUNSET || (!doall && !(v->flags & VEXPORT)))
return NULL;
if (v->rfunc && (v->flags & VFUNCREF) != 0)
return (*v->rfunc)(v) + v->name_len + 1;
return v->text + v->name_len + 1;
}
@ -477,8 +493,12 @@ environment(void)
ep = env = stalloc((nenv + 1) * sizeof *env);
for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) {
for (vp = *vpp ; vp ; vp = vp->next)
if ((vp->flags & (VEXPORT|VUNSET)) == VEXPORT)
*ep++ = vp->text;
if ((vp->flags & (VEXPORT|VUNSET)) == VEXPORT) {
if (vp->rfunc && (vp->flags & VFUNCREF))
*ep++ = (*vp->rfunc)(vp);
else
*ep++ = vp->text;
}
}
*ep = NULL;
return env;
@ -644,7 +664,13 @@ showvars(const char *name, int flag, int show_value, const char *xtra)
out1fmt("%s ", name);
if (xtra)
out1fmt("%s ", xtra);
for (p = vp->text ; *p != '=' ; p++)
p = vp->text;
if (vp->rfunc && (vp->flags & VFUNCREF) != 0) {
p = (*vp->rfunc)(vp);
if (p == NULL)
p = vp->text;
}
for ( ; *p != '=' ; p++)
out1c(*p);
if (!(vp->flags & VUNSET) && show_value) {
out1fmt("=");
@ -812,6 +838,13 @@ mklocal(const char *name, int flags)
unsetvar(name, 0);
else
vp->flags |= flags & (VUNSET|VEXPORT);
if (vp == &line_num) {
if (name[vp->name_len] == '=')
funclinebase = funclineabs -1;
else
funclinebase = 0;
}
}
}
lvp->vp = vp;
@ -838,10 +871,11 @@ poplocalvars(void)
if (vp == NULL) { /* $- saved */
memcpy(optlist, lvp->text, sizeof_optlist);
ckfree(lvp->text);
optschanged();
} else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
(void)unsetvar(vp->text, 0);
} else {
if (vp->func && (vp->flags & VNOFUNC) == 0)
if (vp->func && (vp->flags & (VNOFUNC|VFUNCREF)) == 0)
(*vp->func)(lvp->text + vp->name_len + 1);
if ((vp->flags & VTEXTFIXED) == 0)
ckfree(vp->text);
@ -1003,3 +1037,17 @@ find_var(const char *name, struct var ***vppp, int *lenp)
}
return NULL;
}
char *
get_lineno(struct var *vp)
{
static char lineno_buf[8 + 14];
int ln = line_number;
if (vp->flags & VUNSET)
return NULL;
ln -= funclinebase;
snprintf(lineno_buf, sizeof(lineno_buf), "LINENO=%d", ln);;
return lineno_buf;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: var.h,v 1.29 2017/06/07 04:44:17 kre Exp $ */
/* $NetBSD: var.h,v 1.30 2017/06/07 05:08:32 kre Exp $ */
/*-
* Copyright (c) 1991, 1993
@ -48,16 +48,21 @@
#define VNOFUNC 0x0040 /* don't call the callback function */
#define VNOSET 0x0080 /* do not set variable - just readonly test */
#define VNOEXPORT 0x0100 /* variable may not be exported */
#define VFUNCREF 0x0200 /* the function is called on ref, not set */
struct var;
union var_func_union { /* function to be called when: */
void (*set_func)(const char *); /* variable gets set/unset */
char*(*ref_func)(struct var *); /* variable is referenced */
};
struct var {
struct var *next; /* next entry in hash list */
int flags; /* flags are defined above */
char *text; /* name=value */
int name_len; /* length of name */
void (*func)(const char *);
/* function to be called when */
/* the variable gets set/unset */
union var_func_union v_u; /* function to apply (sometimes) */
};
@ -86,6 +91,10 @@ extern struct var vtermcap;
extern struct var vhistsize;
#endif
extern int line_number;
extern int funclinebase;
extern int funclineabs;
/*
* The following macros access the values of the above variables.
* They have to skip over the name. They return the null string