View Issue Details
| ID | Project | Category | View Status | Date Submitted | Last Update |
|---|---|---|---|---|---|
| 0000490 | tcsh | General | public | 2023-12-01 21:41 | 2024-06-03 14:49 |
| Reporter | MProG10 | Assigned To | |||
| Priority | normal | Severity | feature | Reproducibility | N/A |
| Status | new | Resolution | open | ||
| Summary | 0000490: Introduce 'function' built-in. | ||||
| Description | This is a wrapper around goto and source. The script recurses and searches for a goto label. It's an error for labels not contain an exit to their end. Function calls outside labels are, by default, labeled main. The use is exclusive for scripts. | ||||
| Additional Information | https://github.com/tcsh-org/tcsh/pull/77 | ||||
| Tags | No tags attached. | ||||
|
|
sh.func.c.diff (1,931 bytes)
--- a/sh.func.c
+++ b/sh.func.c
@@ -56,7 +56,6 @@ static void preread (void);
static void doagain (void);
static const char *isrchx (int);
static void search (int, int, Char *);
-static int getword (struct Strbuf *);
static struct wordent *histgetword (struct wordent *);
static void toend (void);
static void xecho (int, Char **);
@@ -742,8 +741,8 @@ isrchx(int n)
}
-static int Stype;
-static Char *Sgoal;
+int Stype;
+Char *Sgoal;
static void
search(int type, int level, Char *goal)
@@ -1000,7 +999,7 @@ past:
return NULL;
}
-static int
+int
getword(struct Strbuf *wp)
{
int found = 0, first;
@@ -1096,6 +1095,13 @@ past:
stderror(ERR_NAME | ERR_NOTFOUND, "label");
break;
+ case TC_EXIT:
+ if (fargv->eof)
+ return (intptr_t) &fargv;
+ setname(short2str(Sgoal));
+ stderror(ERR_NAME | ERR_NOTFOUND, "exit");
+ break;
+
default:
break;
}
@@ -2719,3 +2725,51 @@ getYN(const char *prompt)
continue;
return doit;
}
+
+void
+dofunction(Char **v, struct command *t)
+{
+ if (!dolzero)
+ stderror(ERR_FUNC);
+
+ {
+ int i, j;
+ Char **vh;
+
+ for (i = 0; v[i]; i++)
+ ;
+
+ vh = xmalloc(sizeof(Char [i + 2]));
+ vh[i + 1] = NULL;
+
+ for (j = i--; i; i--, j--) {
+ vh[j] = xmalloc(sizeof(Char [Strlen(v[i]) + 1]));
+ Strcpy(vh[j], v[i]);
+ }
+ vh[1] = xmalloc(sizeof (Char [Strlen(ffile) + 1]));
+ Strcpy(vh[1], ffile);
+ *vh = xmalloc(sizeof (Char [Strlen(*v) + 1]));
+ Strcpy(*vh, *v);
+
+ if (fargv) {
+ fargv->next = malloc(sizeof *fargv);
+ fargv->next->prev = fargv;
+ fargv = fargv->next;
+ } else {
+ fargv = malloc(sizeof *fargv);
+ fargv->prev = NULL;
+ }
+
+ dosource(fargv->v = vh, fargv->t = t);
+ /* Reset STRargv on function exit. */
+ setv(STRargv, NULL, VAR_READWRITE);
+
+ if (fargv->prev) {
+ fargv = fargv->prev;
+ free(fargv->next);
+ } else {
+ free(fargv);
+ fargv = NULL;
+ }
+ }
+}
sh.decls.h.diff (772 bytes)
--- a/sh.decls.h
+++ b/sh.decls.h
@@ -105,6 +105,7 @@ extern void xfree_indirect(void *);
extern void errinit (void);
extern void seterror (unsigned int, ...);
extern void fixerror (void);
+extern void funcerror (Char *, Char *);
extern void stderror (unsigned int, ...)
__attribute__((__noreturn__));
@@ -150,6 +151,7 @@ extern void doend (Char **, struct command *);
extern void doeval (Char **, struct command *);
extern void doexit (Char **, struct command *);
extern void doforeach (Char **, struct command *);
+extern void dofunction (Char **, struct command *);
extern void doglob (Char **, struct command *);
extern void dogoto (Char **, struct command *);
extern void doif (Char **, struct command *);
sh.c.diff (3,194 bytes)
--- a/sh.c
+++ b/sh.c
@@ -111,6 +111,7 @@ int exitset = 0;
static time_t chktim; /* Time mail last checked */
char *progname;
int tcsh;
+struct funcargs *fargv = NULL;
/*
* This preserves the input state of the shell. It is used by
@@ -1768,6 +1769,114 @@ srcunit(int unit, int onlyown, int hflg, Char **av)
cleanup_push(&pintr_disabled, disabled_cleanup);
}
+ /* Functions must have an exit to their end.
+ * if (!fargv->prev) is only true if this is a first function call.
+ * First seek for an ending exit before jumping to the label,
+ * then seek for an ending exit on the requested label.
+ * Function arguments are passed to STRargv.
+ * STRargv is reset after the function is done. */
+ if (fargv) {
+ Char funcexit[] = { 'e', 'x', 'i', 't', 0 },
+ funcmain[] = { 'm', 'a', 'i', 'n', 0 };
+ struct Strbuf aword = Strbuf_INIT;
+ Sgoal = fargv->v[2];
+ Stype = TC_GOTO;
+ fargv->eof = 0;
+
+ if (!fargv->prev)
+ while (1) {
+ (void) getword(&aword);
+ Strbuf_terminate(&aword);
+
+ if (eq(aword.s, funcexit)) {
+ int last = 1;
+
+ while (1) {
+ do {
+ (void) getword(NULL);
+ (void) getword(&aword);
+ Strbuf_terminate(&aword);
+ } while (!aword.s[0]);
+ if (aword.s[0] != ':' && lastchr(aword.s) == ':') {
+ if (!last)
+ funcerror(funcmain, funcexit);
+ break;
+ }
+ if (!eq(aword.s, funcexit)) {
+ last = 0;
+ continue;
+ }
+ last = 1;
+ }
+
+ break;
+ }
+ if (aword.s[0] != ':' && lastchr(aword.s) == ':')
+ funcerror(funcmain, funcexit);
+
+ (void) getword(NULL);
+ }
+
+ setq(STRargv, &fargv->v[3], &shvhed, VAR_READWRITE);
+ dogoto(&fargv->v[1], fargv->t);
+
+ {
+ struct Ain a;
+
+ Stype = TC_EXIT;
+ a.type = TCSH_F_SEEK;
+ btell(&a);
+
+ cleanup_push(&aword, Strbuf_cleanup);
+ while (1) {
+ (void) getword(&aword);
+ Strbuf_terminate(&aword);
+
+ if (eq(aword.s, funcexit)) {
+ int last = 1, eof = 0;
+
+ fargv->eof = 1;
+ while (1) {
+ do {
+ (void) getword(NULL);
+ if ((intptr_t) getword(&aword) == (intptr_t) &fargv) {
+ Strbuf_terminate(&aword);
+ eof = 1;
+ break;
+ }
+ Strbuf_terminate(&aword);
+ } while (!aword.s[0]);
+ if (eof) {
+ if (!last)
+ funcerror(Sgoal, funcexit);
+ break;
+ }
+ if (aword.s[0] != ':' && lastchr(aword.s) == ':') {
+ if (!last)
+ funcerror(Sgoal, funcexit);
+ break;
+ }
+ if (!eq(aword.s, funcexit)) {
+ last = 0;
+ continue;
+ }
+ last = 1;
+ }
+
+ break;
+ }
+ if (aword.s[0] != ':' && lastchr(aword.s) == ':')
+ funcerror(Sgoal, funcexit);
+
+ (void) getword(NULL);
+ }
+
+ bseek(&a);
+ }
+
+ cleanup_until(&aword);
+ }
+
process(0); /* 0 -> blow away on errors */
/* Restore the old state */
@@ -1995,6 +2104,7 @@ process(int catch)
getexit(osetexit);
omark = cleanup_push_mark();
+
for (;;) {
struct command *t;
int hadhist, old_pintr_disabled;
@@ -2179,6 +2289,7 @@ process(int catch)
else
haderr = 1;
}
+
cleanup_pop_mark(omark);
resexit(osetexit);
exitset--;
sh.err.c.diff (929 bytes)
--- a/sh.err.c
+++ b/sh.err.c
@@ -187,7 +187,8 @@ extern int enterhist;
#define ERR_INVALID 133
#define ERR_BADCOLORVAR 134
#define ERR_EOF 135
-#define NO_ERRORS 136
+#define ERR_FUNC 136
+#define NO_ERRORS 137
static const char *elst[NO_ERRORS] INIT_ZERO_STRUCT;
@@ -365,6 +366,7 @@ errinit(void)
elst[ERR_BADJOB] = CSAVS(1, 136, "No such job (badjob)");
elst[ERR_BADCOLORVAR] = CSAVS(1, 137, "Unknown %s color variable '%c%c'");
elst[ERR_EOF] = CSAVS(1, 138, "Unexpected end of file");
+ elst[ERR_FUNC] = CSAVS(1, 139, "Functions are only supported for scripts");
}
/* Cleanup data. */
@@ -654,3 +656,13 @@ stderror(unsigned int id, ...)
reset(); /* Unwind */
}
+
+void
+funcerror(Char *n, Char *msg)
+{
+ char nconv[Strlen(n) + 1],
+ msgconv[Strlen(msg) + 1];
+
+ setname(strcpy(nconv, short2str(n)));
+ stderror(ERR_NAME | ERR_NOTFOUND, strcpy(msgconv, short2str(msg)));
+}
sh.h.diff (486 bytes)
--- a/sh.h
+++ b/sh.h
@@ -1305,5 +1305,16 @@ extern int filec;
#define TEXP_IGNORE 1 /* in ignore, it means to ignore value, just parse */
#define TEXP_NOGLOB 2 /* in ignore, it means not to globone */
+/* Function variable(s) and function(s). */
+extern Char *Sgoal;
+extern int Stype;
+extern struct funcargs {
+ Char **v;
+ struct command *t;
+ struct funcargs *prev,
+ *next;
+ int eof;
+} *fargv;
+extern int getword(struct Strbuf *);
#endif /* _h_sh */
|
|
|
tcsh.man.in.diff (1,096 bytes)
--- a/tcsh.man.in +++ b/tcsh.man.in @@ -5515,6 +5515,30 @@ the loop are executed. If you make a mistake typing in a loop at the terminal you can rub it out. . +.It Ic function Ar label Xo +.Op Ar arg +\&... (+) +.Xc +.Ar label +is a +.Ic goto +label. The shell recurses the current script, +searches for and continues execution after a line of the form +.Dl Ar label Ns No \&: +.Ar arg +is a list of arguments to be passed to +.Ic argv Ns No \&. +Use outside a label is labeled +.Ar main Ns No \&. +It's an error +.Sq Ar label Ns No \&: +not contain an ending +.Ic exit Ns No \&. +It's an error +.Ar main +not contain an ending +.Ic exit Ns No \&. +. .El .Bl -tag -width 6n . @@ -10637,6 +10661,12 @@ and message catalog code to interface to Windows. .br Color ls additions. . +.It Matheus Garcia , +2023. +.br +.Ic function +built-in. +. .El . .Sh THANKS TO @@ -10739,6 +10769,10 @@ cycles or backward .Ic goto Ns s. .Pp +Functions fallthrough if the ending +.Ic exit +is piped from or executed in background. +.Pp Report bugs at .Lk @PACKAGE_BUGREPORT@ preferably with fixes. commands.at.diff (1,110 bytes)
--- a/tests/commands.at
+++ b/tests/commands.at
@@ -636,6 +636,39 @@ c
AT_CLEANUP()
+AT_SETUP([function])
+AT_KEYWORDS([commands])
+
+AT_DATA([function.csh],
+[[
+if ( ! { function test test } ) then
+ echo 'FAIL: '\''function'\'' is not passing arguments!'
+ exit 1
+else if >& /dev/null ( { function test2 } ) then
+ echo 'FAIL: '\''function'\'' is not seeking for an ending exit!'
+ exit 1
+else if >& /dev/null ( ! { function test3 } ) then
+ echo 'FAIL: '\''function'\'' is not seeking for an ending exit on EOF!'
+ exit 1
+endif
+exit
+
+test:
+if ( "$1" == ) exit 1
+exit
+
+test2:
+exit
+echo test
+
+test3:
+exit
+]])
+AT_CHECK([tcsh -f function.csh])
+
+AT_CLEANUP()
+
+
dnl
dnl getspath
dnl
@@ -1870,3 +1903,22 @@ endif
AT_CHECK([tcsh -f time_output.csh], 0, [ignore])
AT_CLEANUP()
+
+AT_SETUP([main function])
+AT_KEYWORDS([commands])
+
+AT_DATA([main.csh],
+[[
+if >& /dev/null ( { function test } ) then
+ echo 'FAIL: '\''function'\'' is not seeking for an ending first exit!'
+ exit 1
+endif
+exit
+echo test
+
+test:
+exit
+]])
+AT_CHECK([tcsh -f main.csh])
+
+AT_CLEANUP()
sh.init.c.diff (325 bytes)
--- a/sh.init.c
+++ b/sh.init.c
@@ -80,6 +80,7 @@ const struct biltins bfunc[] = {
{ "fg", dofg, 0, INF },
{ "filetest", dofiletest, 2, INF },
{ "foreach", doforeach, 3, INF },
+ { "function", dofunction, 1, INF },
#ifdef TCF
{ "getspath", dogetspath, 0, 0 },
{ "getxvers", dogetxvers, 0, 0 },
|
|
|
Prefer srcfile() over dosource() sh.c-2.diff (3,818 bytes)
--- a/sh.c
+++ b/sh.c
@@ -111,6 +111,7 @@ int exitset = 0;
static time_t chktim; /* Time mail last checked */
char *progname;
int tcsh;
+struct funcargs *fargv = NULL;
/*
* This preserves the input state of the shell. It is used by
@@ -141,11 +142,6 @@ struct saved_state {
};
static int srccat (Char *, Char *);
-#ifndef WINNT_NATIVE
-static int srcfile (const char *, int, int, Char **);
-#else
-int srcfile (const char *, int, int, Char **);
-#endif /*WINNT_NATIVE*/
static void srcunit (int, int, int, Char **);
static void mailchk (void);
#ifndef _PATH_DEFPATH
@@ -1543,11 +1539,7 @@ srccat(Char *cp, Char *dp)
/*
* Source to a file putting the file descriptor in a safe place (> 2).
*/
-#ifndef WINNT_NATIVE
-static int
-#else
int
-#endif /*WINNT_NATIVE*/
srcfile(const char *f, int onlyown, int flag, Char **av)
{
int unit;
@@ -1768,6 +1760,114 @@ srcunit(int unit, int onlyown, int hflg, Char **av)
cleanup_push(&pintr_disabled, disabled_cleanup);
}
+ /* Functions must have an exit to their end.
+ * if (!fargv->prev) is only true if this is a first function call.
+ * First seek for an ending exit before jumping to the label,
+ * then seek for an ending exit on the requested label.
+ * Function arguments are passed to STRargv.
+ * STRargv is reset after the function is done. */
+ if (fargv) {
+ Char funcexit[] = { 'e', 'x', 'i', 't', 0 },
+ funcmain[] = { 'm', 'a', 'i', 'n', 0 };
+ struct Strbuf aword = Strbuf_INIT;
+ Sgoal = fargv->v[0];
+ Stype = TC_GOTO;
+ fargv->eof = 0;
+
+ if (!fargv->prev)
+ while (1) {
+ (void) getword(&aword);
+ Strbuf_terminate(&aword);
+
+ if (eq(aword.s, funcexit)) {
+ int last = 1;
+
+ while (1) {
+ do {
+ (void) getword(NULL);
+ (void) getword(&aword);
+ Strbuf_terminate(&aword);
+ } while (!aword.s[0]);
+ if (aword.s[0] != ':' && lastchr(aword.s) == ':') {
+ if (!last)
+ funcerror(funcmain, funcexit);
+ break;
+ }
+ if (!eq(aword.s, funcexit)) {
+ last = 0;
+ continue;
+ }
+ last = 1;
+ }
+
+ break;
+ }
+ if (aword.s[0] != ':' && lastchr(aword.s) == ':')
+ funcerror(funcmain, funcexit);
+
+ (void) getword(NULL);
+ }
+
+ setq(STRargv, &fargv->v[1], &shvhed, VAR_READWRITE);
+ gotolab(fargv->v[0]);
+
+ {
+ struct Ain a;
+
+ Stype = TC_EXIT;
+ a.type = TCSH_F_SEEK;
+ btell(&a);
+
+ cleanup_push(&aword, Strbuf_cleanup);
+ while (1) {
+ (void) getword(&aword);
+ Strbuf_terminate(&aword);
+
+ if (eq(aword.s, funcexit)) {
+ int last = 1, eof = 0;
+
+ fargv->eof = 1;
+ while (1) {
+ do {
+ (void) getword(NULL);
+ if ((intptr_t) getword(&aword) == (intptr_t) &fargv) {
+ Strbuf_terminate(&aword);
+ eof = 1;
+ break;
+ }
+ Strbuf_terminate(&aword);
+ } while (!aword.s[0]);
+ if (eof) {
+ if (!last)
+ funcerror(Sgoal, funcexit);
+ break;
+ }
+ if (aword.s[0] != ':' && lastchr(aword.s) == ':') {
+ if (!last)
+ funcerror(Sgoal, funcexit);
+ break;
+ }
+ if (!eq(aword.s, funcexit)) {
+ last = 0;
+ continue;
+ }
+ last = 1;
+ }
+
+ break;
+ }
+ if (aword.s[0] != ':' && lastchr(aword.s) == ':')
+ funcerror(Sgoal, funcexit);
+
+ (void) getword(NULL);
+ }
+
+ bseek(&a);
+ }
+
+ cleanup_until(&aword);
+ }
+
process(0); /* 0 -> blow away on errors */
/* Restore the old state */
@@ -1995,6 +2095,7 @@ process(int catch)
getexit(osetexit);
omark = cleanup_push_mark();
+
for (;;) {
struct command *t;
int hadhist, old_pintr_disabled;
@@ -2179,6 +2280,7 @@ process(int catch)
else
haderr = 1;
}
+
cleanup_pop_mark(omark);
resexit(osetexit);
exitset--;
sh.h-2.diff (516 bytes)
--- a/sh.h
+++ b/sh.h
@@ -1305,5 +1305,16 @@ extern int filec;
#define TEXP_IGNORE 1 /* in ignore, it means to ignore value, just parse */
#define TEXP_NOGLOB 2 /* in ignore, it means not to globone */
+/* Function variable(s) and function(s). */
+extern Char *Sgoal;
+extern int Stype;
+extern struct funcargs {
+ Char **v;
+ int eof;
+ struct funcargs *prev,
+ *next;
+} *fargv;
+extern int getword(struct Strbuf *);
+extern int srcfile(const char *, int, int, Char **);
#endif /* _h_sh */
sh.func.c-2.diff (1,765 bytes)
--- a/sh.func.c
+++ b/sh.func.c
@@ -56,7 +56,6 @@ static void preread (void);
static void doagain (void);
static const char *isrchx (int);
static void search (int, int, Char *);
-static int getword (struct Strbuf *);
static struct wordent *histgetword (struct wordent *);
static void toend (void);
static void xecho (int, Char **);
@@ -742,8 +741,8 @@ isrchx(int n)
}
-static int Stype;
-static Char *Sgoal;
+int Stype;
+Char *Sgoal;
static void
search(int type, int level, Char *goal)
@@ -1000,7 +999,7 @@ past:
return NULL;
}
-static int
+int
getword(struct Strbuf *wp)
{
int found = 0, first;
@@ -1096,6 +1095,13 @@ past:
stderror(ERR_NAME | ERR_NOTFOUND, "label");
break;
+ case TC_EXIT:
+ if (fargv->eof)
+ return (intptr_t) &fargv;
+ setname(short2str(Sgoal));
+ stderror(ERR_NAME | ERR_NOTFOUND, "exit");
+ break;
+
default:
break;
}
@@ -2719,3 +2725,45 @@ getYN(const char *prompt)
continue;
return doit;
}
+
+void
+dofunction(Char **v, struct command *t)
+{
+ if (!dolzero)
+ stderror(ERR_FUNC);
+
+ if (fargv) {
+ fargv->next = malloc(sizeof *fargv);
+ fargv->next->prev = fargv;
+ fargv = fargv->next;
+ } else {
+ fargv = malloc(sizeof *fargv);
+ fargv->prev = NULL;
+ }
+
+ {
+ int i = 0;
+ Char **vh = NULL;
+
+ for (v++; *v; v++, i++) {
+ vh = xrealloc(vh, sizeof(Char *[i + 2]));
+ vh[i] = xmalloc(sizeof(Char [Strlen(*v) + 1]));
+ Strcpy(vh[i], *v);
+ }
+
+ vh[i] = NULL;
+ fargv->v = vh;
+ }
+
+ srcfile(short2str(ffile), 0, 0, NULL);
+ /* Reset STRargv on function exit. */
+ setv(STRargv, NULL, VAR_READWRITE);
+
+ if (fargv->prev) {
+ fargv = fargv->prev;
+ free(fargv->next);
+ } else {
+ free(fargv);
+ fargv = NULL;
+ }
+}
|
|
|
Functions should work for sourced scripts. Next, make a table of functions so they can be called globally. Currently, it isn't possible to call functions from sourced files other than the current. sh.h-3.diff (720 bytes)
diff --git a/sh.h b/sh.h
index 19bf10d..3d63fea 100644
--- a/sh.h
+++ b/sh.h
@@ -1305,5 +1305,26 @@ extern int filec;
#define TEXP_IGNORE 1 /* in ignore, it means to ignore value, just parse */
#define TEXP_NOGLOB 2 /* in ignore, it means not to globone */
+/* Function variable(s) and function(s). */
+extern Char *Sgoal;
+extern int Stype;
+extern struct funccurr {
+ struct funcargs {
+ Char **v;
+ struct funcargs *prev,
+ *next;
+ } *fargv;
+ struct funcfile {
+ char *file;
+ struct funcfile *prev,
+ *next;
+ } *ffile;
+ char *file;
+ int eof,
+ src,
+ ready;
+} fcurr;
+extern int getword(struct Strbuf *);
+extern int srcfile(const char *, int, int, Char **);
#endif /* _h_sh */
sh.c-3.diff (5,424 bytes)
diff --git a/sh.c b/sh.c
index 2d5565f..ba547a8 100644
--- a/sh.c
+++ b/sh.c
@@ -111,6 +111,7 @@ int exitset = 0;
static time_t chktim; /* Time mail last checked */
char *progname;
int tcsh;
+struct funccurr fcurr;
/*
* This preserves the input state of the shell. It is used by
@@ -141,11 +142,6 @@ struct saved_state {
};
static int srccat (Char *, Char *);
-#ifndef WINNT_NATIVE
-static int srcfile (const char *, int, int, Char **);
-#else
-int srcfile (const char *, int, int, Char **);
-#endif /*WINNT_NATIVE*/
static void srcunit (int, int, int, Char **);
static void mailchk (void);
#ifndef _PATH_DEFPATH
@@ -1543,11 +1539,7 @@ srccat(Char *cp, Char *dp)
/*
* Source to a file putting the file descriptor in a safe place (> 2).
*/
-#ifndef WINNT_NATIVE
-static int
-#else
int
-#endif /*WINNT_NATIVE*/
srcfile(const char *f, int onlyown, int flag, Char **av)
{
int unit;
@@ -1733,6 +1725,7 @@ static void
srcunit(int unit, int onlyown, int hflg, Char **av)
{
struct saved_state st;
+ struct funcargs *fargv;
st.SHIN = -1; /* st_restore checks this */
@@ -1768,6 +1761,119 @@ srcunit(int unit, int onlyown, int hflg, Char **av)
cleanup_push(&pintr_disabled, disabled_cleanup);
}
+ /* Functions must have an exit to their end.
+ * if (!fargv->prev) is only true if this is a first function call.
+ * First seek for an ending exit before jumping to the label,
+ * then seek for an ending exit on the requested label.
+ * Function arguments are passed to STRargv.
+ * STRargv is reset after the function is done. */
+ if (fcurr.ready) {
+ Char funcexit[] = { 'e', 'x', 'i', 't', 0 },
+ *funcmain = fcurr.ffile ?
+ strsave(str2short(fcurr.ffile->file)) :
+ ffile;
+ struct Strbuf aword = Strbuf_INIT;
+
+ Sgoal = fargv->v[0];
+ Stype = TC_GOTO;
+ fcurr.eof = 0;
+ if (!(fargv = fcurr.fargv)->prev || fcurr.src)
+ while (1) {
+ (void) getword(&aword);
+ Strbuf_terminate(&aword);
+
+ if (eq(aword.s, funcexit)) {
+ int last = 1;
+
+ while (1) {
+ do {
+ (void) getword(NULL);
+ (void) getword(&aword);
+ Strbuf_terminate(&aword);
+ } while (!aword.s[0]);
+ if (aword.s[0] != ':' && lastchr(aword.s) == ':') {
+ if (!last)
+ funcerror(funcmain, funcexit);
+ break;
+ }
+ if (!eq(aword.s, funcexit)) {
+ last = 0;
+ continue;
+ }
+ last = 1;
+ }
+ fcurr.src = 0;
+
+ break;
+ }
+ if (aword.s[0] != ':' && lastchr(aword.s) == ':')
+ funcerror(funcmain, funcexit);
+
+ (void) getword(NULL);
+ }
+ if (funcmain != ffile)
+ xfree(funcmain);
+
+ setq(STRargv, &fargv->v[1], &shvhed, VAR_READWRITE);
+ gotolab(fargv->v[0]);
+
+ {
+ struct Ain a;
+
+ Stype = TC_EXIT;
+ a.type = TCSH_F_SEEK;
+ btell(&a);
+
+ cleanup_push(&aword, Strbuf_cleanup);
+ while (1) {
+ (void) getword(&aword);
+ Strbuf_terminate(&aword);
+
+ if (eq(aword.s, funcexit)) {
+ int last = 1, eof = 0;
+
+ fcurr.eof = 1;
+ while (1) {
+ do {
+ (void) getword(NULL);
+ if (getword(&aword) == (1 << 1)) {
+ Strbuf_terminate(&aword);
+ eof = 1;
+ break;
+ }
+ Strbuf_terminate(&aword);
+ } while (!aword.s[0]);
+ if (eof) {
+ if (!last)
+ funcerror(Sgoal, funcexit);
+ break;
+ }
+ if (aword.s[0] != ':' && lastchr(aword.s) == ':') {
+ if (!last)
+ funcerror(Sgoal, funcexit);
+ break;
+ }
+ if (!eq(aword.s, funcexit)) {
+ last = 0;
+ continue;
+ }
+ last = 1;
+ }
+
+ break;
+ }
+ if (aword.s[0] != ':' && lastchr(aword.s) == ':')
+ funcerror(Sgoal, funcexit);
+
+ (void) getword(NULL);
+ }
+
+ bseek(&a);
+ }
+
+ cleanup_until(&aword);
+ }
+
process(0); /* 0 -> blow away on errors */
/* Restore the old state */
@@ -1995,6 +2101,7 @@ process(int catch)
getexit(osetexit);
omark = cleanup_push_mark();
+
for (;;) {
struct command *t;
int hadhist, old_pintr_disabled;
@@ -2179,6 +2286,7 @@ process(int catch)
else
haderr = 1;
}
+
cleanup_pop_mark(omark);
resexit(osetexit);
exitset--;
@@ -2192,6 +2300,7 @@ dosource(Char **t, struct command *c)
Char *f;
int hflg = 0;
char *file;
+ struct funcfile **ffile = NULL;
USE(c);
t++;
@@ -2207,13 +2316,39 @@ dosource(Char **t, struct command *c)
}
f = globone(*t++, G_ERROR);
- file = strsave(short2str(f));
+ fcurr.file = file = strsave(short2str(f));
cleanup_push(file, xfree);
xfree(f);
t = glob_all_or_error(t);
cleanup_push(t, blk_cleanup);
+ if (fcurr.fargv) {
+ if (*(ffile = &fcurr.ffile)) {
+ (*ffile)->next = malloc(sizeof **ffile);
+ (*ffile)->next->prev = *ffile;
+ *ffile = (*ffile)->next;
+ (*ffile)->file = fcurr.file;
+ } else {
+ *ffile = malloc(sizeof **ffile);
+ (*ffile)->prev = NULL;
+ (*ffile)->file = fcurr.file;
+ }
+ }
+
+ fcurr.ready = 0;
+ fcurr.src = 1;
if ((!srcfile(file, 0, hflg, t)) && (!hflg) && (!bequiet))
stderror(ERR_SYSTEM, file, strerror(errno));
+ if (ffile) {
+ if ((*ffile)->prev) {
+ *ffile = (*ffile)->prev;
+ free((*ffile)->next);
+ fcurr.file = (*ffile)->file;
+ } else {
+ fcurr.file = (*ffile)->file = NULL;
+ free(*ffile);
+ *ffile = NULL;
+ }
+ }
cleanup_until(file);
}
sh.func.c-3.diff (2,081 bytes)
diff --git a/sh.func.c b/sh.func.c
index a9c0dd6..185b994 100644
--- a/sh.func.c
+++ b/sh.func.c
@@ -56,7 +56,6 @@ static void preread (void);
static void doagain (void);
static const char *isrchx (int);
static void search (int, int, Char *);
-static int getword (struct Strbuf *);
static struct wordent *histgetword (struct wordent *);
static void toend (void);
static void xecho (int, Char **);
@@ -742,8 +741,8 @@ isrchx(int n)
}
-static int Stype;
-static Char *Sgoal;
+int Stype;
+Char *Sgoal;
static void
search(int type, int level, Char *goal)
@@ -1000,7 +999,7 @@ past:
return NULL;
}
-static int
+int
getword(struct Strbuf *wp)
{
int found = 0, first;
@@ -1096,6 +1095,13 @@ past:
stderror(ERR_NAME | ERR_NOTFOUND, "label");
break;
+ case TC_EXIT:
+ if (fcurr.eof)
+ return 1 << 1;
+ setname(short2str(Sgoal));
+ stderror(ERR_NAME | ERR_NOTFOUND, "exit");
+ break;
+
default:
break;
}
@@ -2719,3 +2725,53 @@ getYN(const char *prompt)
continue;
return doit;
}
+
+void
+dofunction(Char **v, struct command *t)
+{
+ char *file;
+ struct funcargs **fargv;
+
+ if (!dolzero)
+ stderror(ERR_FUNC);
+
+ if (*(fargv = &fcurr.fargv)) {
+ (*fargv)->next = malloc(sizeof **fargv);
+ (*fargv)->next->prev = *fargv;
+ *fargv = (*fargv)->next;
+ } else {
+ *fargv = malloc(sizeof **fargv);
+ (*fargv)->prev = NULL;
+ }
+
+ {
+ int i = 0;
+ Char **vh = NULL;
+
+ for (v++; *v; v++, i++) {
+ vh = xrealloc(vh, sizeof(Char *[i + 2]));
+ vh[i] = xmalloc(sizeof(Char [Strlen(*v) + 1]));
+ Strcpy(vh[i], *v);
+ }
+
+ vh[i] = NULL;
+ (*fargv)->v = vh;
+ }
+ fcurr.ready = 1;
+
+ if (!srcfile(file = fcurr.file ? fcurr.file :
+ strsave(short2str(ffile)), 0, 0, NULL))
+ stderror(ERR_SYSTEM, file, strerror(errno));
+ if (file != fcurr.file)
+ xfree(file);
+ /* Reset STRargv on function exit. */
+ setv(STRargv, NULL, VAR_READWRITE);
+
+ if ((*fargv)->prev) {
+ *fargv = (*fargv)->prev;
+ free((*fargv)->next);
+ } else {
+ free(*fargv);
+ *fargv = NULL;
+ }
+}
|
|
|
I think functions based on pipes makes a simpler feature, as well as allows for use in interactive sessions, resembling Bourne-compatible Shells better. Unlike the goto-based version, functions may only be called if they were previously declared (i.e: no forward jumps), making a similar behavior to Bourne-compatible Shells. This new version relies on a tree derived from variables and aliases. Unlike to aliases and variables, the tree is restrictive. Once a function is declared, may not be redeclared or undeclared. I was afraid this wouldn't work out for some operations, such as loops and gotos, because pipes cannot rewind. Fortunately, I was wrong, and the fact these operations are possible from interactive sessions, from a terminal, makes the assumption just as wrong, though I'm clueless as to how the Shell handles rewinding on unsupported sources. sh.h-4.diff (1,500 bytes)
--- a/sh.h
+++ b/sh.h
@@ -1019,7 +1019,9 @@ EXTERN struct varent {
#define VAR_LAST 64
struct varent *v_link[3]; /* The links, see below */
int v_bal; /* Balance factor */
-} shvhed IZERO_STRUCT, aliases IZERO_STRUCT;
+} shvhed IZERO_STRUCT,
+ aliases IZERO_STRUCT,
+ functions IZERO_STRUCT;
#define v_left v_link[0]
#define v_right v_link[1]
@@ -1266,6 +1268,34 @@ EXTERN nl_catd catd;
extern int filec;
#endif /* FILEC */
+/*
+ * This preserves the input state of the shell. It is used by
+ * st_save and st_restore to manupulate shell state.
+ */
+struct saved_state {
+ int insource;
+ int OLDSTD;
+ int SHIN;
+ int SHOUT;
+ int SHDIAG;
+ int intty;
+ struct whyle *whyles;
+ Char *gointr;
+ Char *arginp;
+ Char *evalp;
+ Char **evalvec;
+ Char *alvecp;
+ Char **alvec;
+ int onelflg;
+ int enterhist;
+ Char **argv;
+ Char **av;
+ Char HIST;
+ int cantell;
+ struct Bin B;
+ int justpr;
+};
+
#include "sh.decls.h"
/*
* Since on some machines characters are unsigned, and the signed
@@ -1305,5 +1335,8 @@ extern int filec;
#define TEXP_IGNORE 1 /* in ignore, it means to ignore value, just parse */
#define TEXP_NOGLOB 2 /* in ignore, it means not to globone */
+extern int fpipe; /* Write end of a pipe used by dofunction. */
+extern Char *fdecl; /* Pointer to function declaration
+ * used by dofunction. */
#endif /* _h_sh */
sh.init.c-2.diff (599 bytes)
--- a/sh.init.c
+++ b/sh.init.c
@@ -80,6 +80,7 @@ const struct biltins bfunc[] = {
{ "fg", dofg, 0, INF },
{ "filetest", dofiletest, 2, INF },
{ "foreach", doforeach, 3, INF },
+ { "function", dofunction, 0, INF },
#ifdef TCF
{ "getspath", dogetspath, 0, 0 },
{ "getxvers", dogetxvers, 0, 0 },
@@ -122,6 +123,7 @@ const struct biltins bfunc[] = {
{ "pushd", dopushd, 0, INF },
{ "rehash", dohash, 0, 3 },
{ "repeat", dorepeat, 2, INF },
+ { "return", doreturn, 0, 0 },
#ifdef apollo
{ "rootnode", dorootnode, 1, 1 },
#endif /* apollo */
sh.lex.c.diff (353 bytes)
--- a/sh.lex.c
+++ b/sh.lex.c
@@ -1717,6 +1717,11 @@ bgetc(void)
buf = (int) feobp / BUFSIZE;
balloc(buf);
roomleft = BUFSIZE - off;
+ if (fpipe) {
+ if (!*fdecl)
+ return CHAR_ERR;
+ (void) xwrite(fpipe, fdecl++, (size_t) 1);
+ }
c = wide_read(SHIN, fbuf[buf] + off, roomleft, 0);
if (c > 0)
feobp += c;
tcsh.man.in-2.diff (1,354 bytes)
--- a/tcsh.man.in +++ b/tcsh.man.in @@ -5424,12 +5424,14 @@ messages are verbose. .It Ic end .It Ic endif .It Ic endsw +.It Ic return See the description of the .Ic foreach , .Ic if , .Ic switch , +.Ic while , and -.Ic while +.Ic return statements below. . .El @@ -5521,6 +5523,36 @@ the loop are executed. If you make a mistake typing in a loop at the terminal you can rub it out. . +.It Ic function No (+) +.It Ic function Ar name No (+) +.It Ic \&... +.It Ic return +.It Ic function Ar name Xo +.Op Ar arg No ... +(+) +.Xc +The first form of the command prints the value of all shell functions. +.Pp +The second form declares a function +.Ar name Ns No . +A declaration ends when a +.Ic return +is matched. (Both +.Ic function +and +.Ic return +must appear alone on separate lines.) +May not be declared otherwise, and declared +functions may not be redeclared or undeclared. +.Pp +The third form calls a function +.Ar name Ns No , +optionally, preceded by +.Ar arg Ns No , +which is a list of arguments to be passed. +Function calls may be nested or recursive, +but too deep a nest or recursion will raise an error. +. .El .Bl -tag -width 6n . @@ -10642,6 +10674,12 @@ and message catalog code to interface to Windows. .br Color ls additions. . +.It Matheus Garcia , +2023. +.br +.Ic function +built-in. +. .El . .Sh THANKS TO sh.c-4.diff (1,636 bytes)
--- a/sh.c
+++ b/sh.c
@@ -112,34 +112,6 @@ static time_t chktim; /* Time mail last checked */
char *progname;
int tcsh;
-/*
- * This preserves the input state of the shell. It is used by
- * st_save and st_restore to manupulate shell state.
- */
-struct saved_state {
- int insource;
- int OLDSTD;
- int SHIN;
- int SHOUT;
- int SHDIAG;
- int intty;
- struct whyle *whyles;
- Char *gointr;
- Char *arginp;
- Char *evalp;
- Char **evalvec;
- Char *alvecp;
- Char **alvec;
- int onelflg;
- int enterhist;
- Char **argv;
- Char **av;
- Char HIST;
- int cantell;
- struct Bin B;
- int justpr;
-};
-
static int srccat (Char *, Char *);
#ifndef WINNT_NATIVE
static int srcfile (const char *, int, int, Char **);
@@ -152,10 +124,6 @@ static void mailchk (void);
static Char **defaultpath (void);
#endif
static void record (void);
-static void st_save (struct saved_state *, int, int,
- Char **, Char **);
-static void st_restore (void *);
-
int main (int, char **);
#ifndef LOCALEDIR
@@ -1569,7 +1537,7 @@ srcfile(const char *f, int onlyown, int flag, Char **av)
* Save the shell state, and establish new argument vector, and new input
* fd.
*/
-static void
+void
st_save(struct saved_state *st, int unit, int hflg, Char **al, Char **av)
{
st->insource = insource;
@@ -1671,7 +1639,7 @@ st_save(struct saved_state *st, int unit, int hflg, Char **al, Char **av)
/*
* Restore the shell to a saved state
*/
-static void
+void
st_restore(void *xst)
{
struct saved_state *st;
sh.decls.h-2.diff (1,243 bytes)
--- a/sh.decls.h +++ b/sh.decls.h @@ -52,6 +52,8 @@ extern void done (int) __attribute__((__noreturn__)); extern void xexit (int) __attribute__((__noreturn__)); #endif extern int grabpgrp (int, pid_t); +extern void st_save (struct saved_state *, int, int, Char **, Char **); +extern void st_restore (void *); /* * sh.dir.c @@ -150,6 +152,7 @@ extern void doend (Char **, struct command *); extern void doeval (Char **, struct command *); extern void doexit (Char **, struct command *); extern void doforeach (Char **, struct command *); +extern void dofunction (Char **, struct command *); extern void doglob (Char **, struct command *); extern void dogoto (Char **, struct command *); extern void doif (Char **, struct command *); @@ -164,6 +167,7 @@ extern void dohup (Char **, struct command *); extern void doonintr (Char **, struct command *); extern void doprintenv (Char **, struct command *); extern void dorepeat (Char **, struct command *); +extern void doreturn (Char **, struct command *); extern void dofiletest (Char **, struct command *); extern void dosetenv (Char **, struct command *); extern void dosuspend (Char **, struct command *); sh.err.c-2.diff (1,054 bytes)
--- a/sh.err.c
+++ b/sh.err.c
@@ -188,7 +188,12 @@ extern int enterhist;
#define ERR_BADCOLORVAR 134
#define ERR_EOF 135
#define ERR_UNAVAILABLE 136
-#define NO_ERRORS 137
+#define ERR_FUNC 137
+#define ERR_RETURN 138
+#define ERR_FUNCBEGIN 139
+#define ERR_FUNCALNUM 140
+#define ERR_RECURSION 141
+#define NO_ERRORS 142
static const char *elst[NO_ERRORS] INIT_ZERO_STRUCT;
@@ -367,7 +372,11 @@ errinit(void)
elst[ERR_BADCOLORVAR] = CSAVS(1, 137, "Unknown %s color variable '%c%c'");
elst[ERR_EOF] = CSAVS(1, 138, "Unexpected end of file");
elst[ERR_UNAVAILABLE] = CSAVS(1, 139, "%s: Feature is not available for this platform");
-
+ elst[ERR_FUNC] = CSAVS(1, 140, "%S: Undeclared function");
+ elst[ERR_RETURN] = CSAVS(1, 141, "Not in a declaration");
+ elst[ERR_FUNCBEGIN] = CSAVS(1, 142, "Function name must begin with a letter");
+ elst[ERR_FUNCALNUM] = CSAVS(1, 143, "Function name must contain alphanumeric characters");
+ elst[ERR_RECURSION] = CSAVS(1, 144, "Recursion too deep");
}
/* Cleanup data. */
sh.func.c-4.diff (3,563 bytes)
--- a/sh.func.c
+++ b/sh.func.c
@@ -1096,6 +1096,11 @@ past:
stderror(ERR_NAME | ERR_NOTFOUND, "label");
break;
+ case TC_EXIT:
+ setname(short2str(Sgoal));
+ stderror(ERR_NAME | ERR_NOTFOUND, "return");
+ break;
+
default:
break;
}
@@ -2719,3 +2724,141 @@ getYN(const char *prompt)
continue;
return doit;
}
+
+int fpipe;
+Char *fdecl;
+
+void
+dofunction(Char **v, struct command *c)
+{
+ if (*++v == NULL) {
+ plist(&functions, VAR_READONLY);
+
+ return;
+ }
+ Sgoal = *v++;
+ Stype = TC_EXIT;
+ {
+ static int l;
+ int pv[2];
+ struct saved_state st;
+ struct varent *varp;
+ Char *p;
+
+ if (!letter(*(p = Sgoal)))
+ stderror(ERR_NAME | ERR_FUNCBEGIN);
+ while (*++p)
+ if (!alnum(*p))
+ stderror(ERR_NAME | ERR_FUNCALNUM);
+ if ((varp = adrof1(Sgoal, &functions))) {
+ jmp_buf_t oldexit;
+ int pvsav, ohaderr;
+ Char *fsav;
+
+ if (l == 16)
+ stderror(ERR_RECURSION);
+ mypipe(pv);
+ st_save(&st, pv[0], 0, NULL, v);
+ pvsav = fpipe;
+ fpipe = pv[1];
+ fsav = fdecl;
+ fdecl = *varp->vec;
+ ohaderr = haderr;
+ getexit(oldexit);
+ l++;
+ if (!setexit())
+ process(0);
+ resexit(oldexit);
+ haderr = ohaderr;
+ st_restore(&st);
+ xclose(pv[1]);
+ fpipe = pvsav;
+ fdecl = fsav;
+ l--;
+
+ return;
+ }
+ if (*v || c->t_dflg & (F_PIPEIN | F_PIPEOUT) ||
+ c->t_dlef || c->t_drit || !isatty(OLDSTD))
+ stderror(ERR_FUNC, Sgoal);
+ {
+ Char funcexit[] = { 'r', 'e', 't', 'u', 'r', 'n', '\0' },
+ *(*varvec)[2];
+ struct Strbuf aword = Strbuf_INIT,
+ func = Strbuf_INIT;
+ struct wordent *histent = NULL,
+ *ohistent = NULL;
+
+ cleanup_push(&aword, Strbuf_cleanup);
+ while (1) {
+ if (intty) {
+ histent = xmalloc(sizeof(*histent));
+ ohistent = xmalloc(sizeof(*histent));
+ ohistent->word = STRNULL;
+ ohistent->next = histent;
+ histent->prev = ohistent;
+ }
+ if (intty && fseekp == feobp && aret == TCSH_F_SEEK)
+ printprompt(1, bname);
+ (void) getword(&aword);
+ Strbuf_terminate(&aword);
+ if (intty && Strlen(aword.s) > 0) {
+ histent->word = Strsave(aword.s);
+ histent->next = xmalloc(sizeof(*histent));
+ histent->next->prev = histent;
+ histent = histent->next;
+ }
+
+ if (eq(aword.s, funcexit))
+ break;
+ Strbuf_append(&func, aword.s);
+ Strbuf_append1(&func, ' ');
+ while (getword(&aword)) {
+ Strbuf_terminate(&aword);
+ if (intty && Strlen(aword.s) > 0) {
+ histent->word = Strsave(aword.s);
+ histent->next = xmalloc(sizeof(*histent));
+ histent->next->prev = histent;
+ histent = histent->next;
+ }
+ Strbuf_append(&func, aword.s);
+ Strbuf_append1(&func, ' ');
+ }
+ func.s[func.len - 1] = '\n';
+
+ if (intty) {
+ ohistent->prev = histgetword(histent);
+ ohistent->prev->next = ohistent;
+ savehist(ohistent, 0);
+ freelex(ohistent);
+ xfree(ohistent);
+ } else
+ (void) getword(NULL);
+ }
+
+ if (intty) {
+ ohistent->prev = histgetword(histent);
+ ohistent->prev->next = ohistent;
+ savehist(ohistent, 0);
+ freelex(ohistent);
+ xfree(ohistent);
+ }
+ cleanup_until(&aword);
+ if (!func.len)
+ return;
+ func.s[--func.len] = 0;
+ **(varvec = xmalloc(sizeof *varvec)) = func.s;
+ *varvec[1] = NULL;
+ setq(Sgoal, *varvec, &functions, VAR_READONLY);
+ }
+ }
+}
+
+void
+doreturn(Char **v, struct command *c)
+{
+ USE(c);
+ USE(v);
+
+ stderror(ERR_NAME | ERR_RETURN);
+}
|
|
|
Recursion/nest. |
| Date Modified | Username | Field | Change |
|---|---|---|---|
| 2023-12-01 21:41 | MProG10 | New Issue | |
| 2023-12-02 00:49 | MProG10 | Note Added: 0003981 | |
| 2023-12-02 00:49 | MProG10 | File Added: sh.decls.h.diff | |
| 2023-12-02 00:49 | MProG10 | File Added: sh.c.diff | |
| 2023-12-02 00:49 | MProG10 | File Added: sh.err.c.diff | |
| 2023-12-02 00:49 | MProG10 | File Added: sh.func.c.diff | |
| 2023-12-02 00:49 | MProG10 | File Added: sh.h.diff | |
| 2023-12-02 03:03 | MProG10 | Note Added: 0003982 | |
| 2023-12-02 03:03 | MProG10 | File Added: sh.init.c.diff | |
| 2023-12-02 03:03 | MProG10 | File Added: tcsh.man.in.diff | |
| 2023-12-02 03:03 | MProG10 | File Added: commands.at.diff | |
| 2023-12-03 19:44 | MProG10 | Note Added: 0003985 | |
| 2023-12-03 19:44 | MProG10 | File Added: sh.h-2.diff | |
| 2023-12-03 19:44 | MProG10 | File Added: sh.func.c-2.diff | |
| 2023-12-03 19:44 | MProG10 | File Added: sh.c-2.diff | |
| 2024-02-14 14:57 | MProG10 | Note Added: 0004011 | |
| 2024-02-14 14:57 | MProG10 | File Added: sh.c-3.diff | |
| 2024-02-14 14:57 | MProG10 | File Added: sh.h-3.diff | |
| 2024-02-14 14:57 | MProG10 | File Added: sh.func.c-3.diff | |
| 2024-06-03 14:34 | MProG10 | Note Added: 0004052 | |
| 2024-06-03 14:34 | MProG10 | File Added: sh.init.c-2.diff | |
| 2024-06-03 14:34 | MProG10 | File Added: sh.lex.c.diff | |
| 2024-06-03 14:34 | MProG10 | File Added: tcsh.man.in-2.diff | |
| 2024-06-03 14:34 | MProG10 | File Added: sh.c-4.diff | |
| 2024-06-03 14:34 | MProG10 | File Added: sh.decls.h-2.diff | |
| 2024-06-03 14:34 | MProG10 | File Added: sh.err.c-2.diff | |
| 2024-06-03 14:34 | MProG10 | File Added: sh.func.c-4.diff | |
| 2024-06-03 14:34 | MProG10 | File Added: sh.h-4.diff | |
| 2024-06-03 14:49 | MProG10 | Note Added: 0004053 |