From faab6cc41677233821d127b03341ecf9df6b5d1a Mon Sep 17 00:00:00 2001 From: Krush206 <37114863+Krush206@users.noreply.github.com> Date: Thu, 6 Jul 2023 23:42:37 -0300 Subject: [PATCH 01/27] Introduce 'function' built-in As requested in https://github.com/tcsh-org/tcsh/issues/4, here's my proposal. This is a wrapper around goto and source. The script recurses itself 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. This was tested sparsely, and may contain bugs I haven't faced, but is working as expected. One bug to be noted is that pipes don't give up on errors. This is possibly due to forking. I noticed Tcsh has a built-in function command, but I can't trace the code. Said built-in function command is evaluated before mine, thus those who attempt to execute it won't get the correct error message. --- sh.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ sh.decls.h | 2 ++ sh.err.c | 18 +++++++++++++-- sh.func.c | 50 +++++++++++++++++++++++++++++++++++---- sh.h | 10 ++++++++ sh.init.c | 1 + 6 files changed, 143 insertions(+), 6 deletions(-) diff --git a/sh.c b/sh.c index 2d5565fc..9bc5b88c 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 funcargs *fargv = NULL; /* * This preserves the input state of the shell. It is used by @@ -1995,6 +1996,59 @@ process(int catch) getexit(osetexit); omark = cleanup_push_mark(); + + /* If this is a function, setup STRargv and invoke goto. */ + if (fargv) { + int funcdelim = 0; + Char funcexit[] = { 'e', 'x', 'i', 't', 0 }, + funcmain[] = { 'm', 'a', 'i', 'n', 0 }; + struct Strbuf aword = Strbuf_INIT; + cleanup_push(&aword, Strbuf_cleanup); + Sgoal = funcmain; + Stype = TC_GOTO; + + if (!fargv->prev) + while (!funcdelim) { + (void) getword(&aword); + Strbuf_terminate(&aword); + + if (aword.s[0] != ':' && lastchr(aword.s) == ':') + funcerror(funcmain, funcexit); + else if (eq(aword.s, funcexit)) + funcdelim = 1; + + (void) getword(NULL); + } + + setq(STRargv, &fargv->v[3], &shvhed, 0); + dogoto(&fargv->v[1], fargv->t); + + { + struct Ain a; + + cleanup_push(&aword, Strbuf_cleanup); + Sgoal = fargv->v[2]; + Stype = TC_EXIT; + a.type = TCSH_F_SEEK; + btell(&a); + funcdelim = 0; + + while (!funcdelim) { + (void) getword(&aword); + Strbuf_terminate(&aword); + + if (aword.s[0] != ':' && lastchr(aword.s) == ':') + funcerror(fargv->v[2], funcexit); + else if (eq(aword.s, funcexit)) + funcdelim = 1; + + (void) getword(NULL); + } + + bseek(&a); + } + } + for (;;) { struct command *t; int hadhist, old_pintr_disabled; @@ -2179,6 +2233,20 @@ process(int catch) else haderr = 1; } + + if (fargv) { + /* Reset STRargv on function exit. */ + setv(STRargv, NULL, 0); + + if (fargv->prev) + { + fargv = fargv->prev; + free(fargv->next); + } + else + free(fargv); + } + cleanup_pop_mark(omark); resexit(osetexit); exitset--; diff --git a/sh.decls.h b/sh.decls.h index a863fede..bd71890d 100644 --- 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 *); diff --git a/sh.err.c b/sh.err.c index 9d9600ae..9830c151 100644 --- a/sh.err.c +++ b/sh.err.c @@ -188,7 +188,8 @@ extern int enterhist; #define ERR_BADCOLORVAR 134 #define ERR_EOF 135 #define ERR_UNAVAILABLE 136 -#define NO_ERRORS 137 +#define ERR_DOLFUNC 137 +#define NO_ERRORS 138 static const char *elst[NO_ERRORS] INIT_ZERO_STRUCT; @@ -367,7 +368,7 @@ 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_DOLFUNC] = CSAVS(1, 140, "Functions are only supported for scripts"); } /* Cleanup data. */ @@ -657,3 +658,16 @@ stderror(unsigned int id, ...) reset(); /* Unwind */ } + +void +funcerror(Char *n, Char *msg) +{ + char nconv[Strlen(n) + 1], + msgconv[Strlen(msg) + 1]; + + strcpy(nconv, short2str(n)); + strcpy(msgconv, short2str(msg)); + + setname(nconv); + stderror(ERR_NAME | ERR_NOTFOUND, msgconv); +} diff --git a/sh.func.c b/sh.func.c index a9c0dd6f..7f87882e 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 @@ histgetword(struct wordent *histent) return NULL; } -static int +int getword(struct Strbuf *wp) { int found = 0, first; @@ -1096,6 +1095,11 @@ getword(struct Strbuf *wp) stderror(ERR_NAME | ERR_NOTFOUND, "label"); break; + case TC_EXIT: + setname(short2str(Sgoal)); + stderror(ERR_NAME | ERR_NOTFOUND, "exit"); + break; + default: break; } @@ -2719,3 +2723,41 @@ getYN(const char *prompt) continue; return doit; } + +void +dofunction(Char **v, struct command *t) +{ + if (!ffile) + stderror(ERR_DOLFUNC); + + { + int i, j; + Char **vh; + + for (i = 0; v[i]; i++) + ; + + vh = xmalloc((i + 2) * sizeof(Char *)); + vh[i + 1] = NULL; + + for (j = i--; i; i--, j--) { + vh[j] = xmalloc(((Strlen(v[i]) + 1) * sizeof(Char))); + Strcpy(vh[j], v[i]); + } + vh[1] = xmalloc(Strlen(ffile) + 1); + Strcpy(vh[1], ffile); + *vh = xmalloc(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); + } +} diff --git a/sh.h b/sh.h index 19bf10d6..2d54252c 100644 --- a/sh.h +++ b/sh.h @@ -1305,5 +1305,15 @@ 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; +} *fargv; +extern int getword(struct Strbuf *); #endif /* _h_sh */ diff --git a/sh.init.c b/sh.init.c index 737afbc7..37b09d01 100644 --- 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 }, From b683769f6f7f239c0fbf482c261f469865755353 Mon Sep 17 00:00:00 2001 From: Krush206 <37114863+Krush206@users.noreply.github.com> Date: Fri, 7 Jul 2023 14:45:07 -0300 Subject: [PATCH 02/27] sh.c: Sgoal shouldn't be assigned funcmain --- sh.c | 5 ++--- sh.err.c | 7 ++----- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/sh.c b/sh.c index 9bc5b88c..90ae79bb 100644 --- a/sh.c +++ b/sh.c @@ -2004,7 +2004,7 @@ process(int catch) funcmain[] = { 'm', 'a', 'i', 'n', 0 }; struct Strbuf aword = Strbuf_INIT; cleanup_push(&aword, Strbuf_cleanup); - Sgoal = funcmain; + Sgoal = fargv->v[2]; Stype = TC_GOTO; if (!fargv->prev) @@ -2027,7 +2027,6 @@ process(int catch) struct Ain a; cleanup_push(&aword, Strbuf_cleanup); - Sgoal = fargv->v[2]; Stype = TC_EXIT; a.type = TCSH_F_SEEK; btell(&a); @@ -2038,7 +2037,7 @@ process(int catch) Strbuf_terminate(&aword); if (aword.s[0] != ':' && lastchr(aword.s) == ':') - funcerror(fargv->v[2], funcexit); + funcerror(Sgoal, funcexit); else if (eq(aword.s, funcexit)) funcdelim = 1; diff --git a/sh.err.c b/sh.err.c index 9830c151..3d2ec57f 100644 --- a/sh.err.c +++ b/sh.err.c @@ -665,9 +665,6 @@ funcerror(Char *n, Char *msg) char nconv[Strlen(n) + 1], msgconv[Strlen(msg) + 1]; - strcpy(nconv, short2str(n)); - strcpy(msgconv, short2str(msg)); - - setname(nconv); - stderror(ERR_NAME | ERR_NOTFOUND, msgconv); + setname(strcpy(nconv, short2str(n))); + stderror(ERR_NAME | ERR_NOTFOUND, strcpy(msgconv, short2str(msg))); } From 4d90ff383ba33abb271bfd98757d9a25732488d6 Mon Sep 17 00:00:00 2001 From: Krush206 <37114863+Krush206@users.noreply.github.com> Date: Sat, 8 Jul 2023 10:38:14 -0300 Subject: [PATCH 03/27] sh.c: cleanup_push() raises SIGSEGV --- sh.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sh.c b/sh.c index 90ae79bb..bb5ea9d8 100644 --- a/sh.c +++ b/sh.c @@ -2003,7 +2003,6 @@ process(int catch) Char funcexit[] = { 'e', 'x', 'i', 't', 0 }, funcmain[] = { 'm', 'a', 'i', 'n', 0 }; struct Strbuf aword = Strbuf_INIT; - cleanup_push(&aword, Strbuf_cleanup); Sgoal = fargv->v[2]; Stype = TC_GOTO; @@ -2020,13 +2019,12 @@ process(int catch) (void) getword(NULL); } - setq(STRargv, &fargv->v[3], &shvhed, 0); + setq(STRargv, &fargv->v[3], &shvhed, VAR_READWRITE); dogoto(&fargv->v[1], fargv->t); { struct Ain a; - cleanup_push(&aword, Strbuf_cleanup); Stype = TC_EXIT; a.type = TCSH_F_SEEK; btell(&a); @@ -2235,7 +2233,7 @@ process(int catch) if (fargv) { /* Reset STRargv on function exit. */ - setv(STRargv, NULL, 0); + setv(STRargv, NULL, VAR_READWRITE); if (fargv->prev) { From 715a88290dab2533f589e97ed284f748d8c0d83d Mon Sep 17 00:00:00 2001 From: Krush206 <37114863+Krush206@users.noreply.github.com> Date: Sat, 8 Jul 2023 15:11:10 -0300 Subject: [PATCH 04/27] fargv should be freed from dofunction() --- sh.c | 20 ++++++-------------- sh.func.c | 8 ++++++++ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/sh.c b/sh.c index bb5ea9d8..78eddc03 100644 --- a/sh.c +++ b/sh.c @@ -1997,7 +1997,12 @@ process(int catch) getexit(osetexit); omark = cleanup_push_mark(); - /* If this is a function, setup STRargv and invoke goto. */ + /* 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 exit before jumping to the label, + * then seek for an exit on the requested label. + * Function arguments are passed to STRargv. + * STRargv is reset after the function is done. */ if (fargv) { int funcdelim = 0; Char funcexit[] = { 'e', 'x', 'i', 't', 0 }, @@ -2231,19 +2236,6 @@ process(int catch) haderr = 1; } - if (fargv) { - /* Reset STRargv on function exit. */ - setv(STRargv, NULL, VAR_READWRITE); - - if (fargv->prev) - { - fargv = fargv->prev; - free(fargv->next); - } - else - free(fargv); - } - cleanup_pop_mark(omark); resexit(osetexit); exitset--; diff --git a/sh.func.c b/sh.func.c index 7f87882e..6cb26d5f 100644 --- a/sh.func.c +++ b/sh.func.c @@ -2759,5 +2759,13 @@ dofunction(Char **v, struct command *t) } 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); } } From 5e37bfce72113dce4c1772a57fff1ed2d5c29cd9 Mon Sep 17 00:00:00 2001 From: Krush206 <37114863+Krush206@users.noreply.github.com> Date: Sat, 5 Aug 2023 15:15:58 -0300 Subject: [PATCH 05/27] Move function tests to srcunit() --- sh.c | 108 +++++++++++++++++++++++++++++------------------------------ 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/sh.c b/sh.c index 78eddc03..5d711212 100644 --- a/sh.c +++ b/sh.c @@ -1769,6 +1769,60 @@ 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 exit before jumping to the label, + * then seek for an exit on the requested label. + * Function arguments are passed to STRargv. + * STRargv is reset after the function is done. */ + if (fargv) { + int funcdelim = 0; + 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; + + if (!fargv->prev) + while (!funcdelim) { + (void) getword(&aword); + Strbuf_terminate(&aword); + + if (aword.s[0] != ':' && lastchr(aword.s) == ':') + funcerror(funcmain, funcexit); + else if (eq(aword.s, funcexit)) + funcdelim = 1; + + (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); + funcdelim = 0; + + while (!funcdelim) { + (void) getword(&aword); + Strbuf_terminate(&aword); + + if (aword.s[0] != ':' && lastchr(aword.s) == ':') + funcerror(Sgoal, funcexit); + else if (eq(aword.s, funcexit)) + funcdelim = 1; + + (void) getword(NULL); + } + + bseek(&a); + } + } + process(0); /* 0 -> blow away on errors */ /* Restore the old state */ @@ -1997,60 +2051,6 @@ process(int catch) getexit(osetexit); omark = cleanup_push_mark(); - /* 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 exit before jumping to the label, - * then seek for an exit on the requested label. - * Function arguments are passed to STRargv. - * STRargv is reset after the function is done. */ - if (fargv) { - int funcdelim = 0; - 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; - - if (!fargv->prev) - while (!funcdelim) { - (void) getword(&aword); - Strbuf_terminate(&aword); - - if (aword.s[0] != ':' && lastchr(aword.s) == ':') - funcerror(funcmain, funcexit); - else if (eq(aword.s, funcexit)) - funcdelim = 1; - - (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); - funcdelim = 0; - - while (!funcdelim) { - (void) getword(&aword); - Strbuf_terminate(&aword); - - if (aword.s[0] != ':' && lastchr(aword.s) == ':') - funcerror(Sgoal, funcexit); - else if (eq(aword.s, funcexit)) - funcdelim = 1; - - (void) getword(NULL); - } - - bseek(&a); - } - } - for (;;) { struct command *t; int hadhist, old_pintr_disabled; From 235b358e0023aa2e2f0c563937a843f289c40684 Mon Sep 17 00:00:00 2001 From: Krush206 <37114863+Krush206@users.noreply.github.com> Date: Mon, 20 Nov 2023 21:25:55 -0300 Subject: [PATCH 06/27] Better function parsing --- sh.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++-------- sh.func.c | 2 ++ sh.h | 1 + 3 files changed, 66 insertions(+), 10 deletions(-) diff --git a/sh.c b/sh.c index 5d711212..5b34e499 100644 --- a/sh.c +++ b/sh.c @@ -1771,27 +1771,48 @@ srcunit(int unit, int onlyown, int hflg, Char **av) /* 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 exit before jumping to the label, - * then seek for an exit on the requested label. + * 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) { - int funcdelim = 0; 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 (!funcdelim) { + 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); - else if (eq(aword.s, funcexit)) - funcdelim = 1; (void) getword(NULL); } @@ -1805,22 +1826,54 @@ srcunit(int unit, int onlyown, int hflg, Char **av) Stype = TC_EXIT; a.type = TCSH_F_SEEK; btell(&a); - funcdelim = 0; - while (!funcdelim) { + 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); - else if (eq(aword.s, funcexit)) - funcdelim = 1; (void) getword(NULL); } bseek(&a); } + + cleanup_push(&aword, Strbuf_cleanup); } process(0); /* 0 -> blow away on errors */ diff --git a/sh.func.c b/sh.func.c index 6cb26d5f..a1b6fc45 100644 --- a/sh.func.c +++ b/sh.func.c @@ -1096,6 +1096,8 @@ getword(struct Strbuf *wp) break; case TC_EXIT: + if (fargv->eof) + return (intptr_t) &fargv; setname(short2str(Sgoal)); stderror(ERR_NAME | ERR_NOTFOUND, "exit"); break; diff --git a/sh.h b/sh.h index 2d54252c..bd06d9bd 100644 --- a/sh.h +++ b/sh.h @@ -1313,6 +1313,7 @@ extern struct funcargs { struct command *t; struct funcargs *prev, *next; + int eof; } *fargv; extern int getword(struct Strbuf *); From 4a54e0586779b8b69f60bfaa03951f2337f59a95 Mon Sep 17 00:00:00 2001 From: Krush206 <37114863+Krush206@users.noreply.github.com> Date: Tue, 21 Nov 2023 14:39:42 -0300 Subject: [PATCH 07/27] Create tests for 'function' built-in --- tests/commands.at | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/tests/commands.at b/tests/commands.at index 48418cca..99049565 100644 --- 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() From 5bc9252c93f73c93f075af4fda8127e0ff9ebce2 Mon Sep 17 00:00:00 2001 From: Krush206 <37114863+Krush206@users.noreply.github.com> Date: Tue, 21 Nov 2023 17:42:13 -0300 Subject: [PATCH 08/27] sh.c: call cleanup_push() properly --- sh.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sh.c b/sh.c index 5b34e499..fbe84f46 100644 --- a/sh.c +++ b/sh.c @@ -1827,6 +1827,7 @@ srcunit(int unit, int onlyown, int hflg, Char **av) a.type = TCSH_F_SEEK; btell(&a); + cleanup_push(&aword, Strbuf_cleanup); while (1) { (void) getword(&aword); Strbuf_terminate(&aword); @@ -1873,7 +1874,7 @@ srcunit(int unit, int onlyown, int hflg, Char **av) bseek(&a); } - cleanup_push(&aword, Strbuf_cleanup); + cleanup_until(&aword); } process(0); /* 0 -> blow away on errors */ From b1e413528d256ab8c0b1d7db6e4223bdaf621b82 Mon Sep 17 00:00:00 2001 From: Krush206 <37114863+Krush206@users.noreply.github.com> Date: Tue, 21 Nov 2023 16:39:13 -0300 Subject: [PATCH 09/27] Documentation for 'function' built-in --- tcsh.man.in | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tcsh.man.in b/tcsh.man.in index bde78ca3..efa49a69 100644 --- a/tcsh.man.in +++ b/tcsh.man.in @@ -5521,6 +5521,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 . @@ -10642,6 +10666,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 @@ -10744,6 +10774,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. From ede350d577e7eacec5011e845dcdd2e5632a836f Mon Sep 17 00:00:00 2001 From: Krush206 <37114863+Krush206@users.noreply.github.com> Date: Tue, 21 Nov 2023 18:38:00 -0300 Subject: [PATCH 10/27] Rename ERR_DOLFUNC to ERR_FUNC --- sh.err.c | 4 ++-- sh.func.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sh.err.c b/sh.err.c index 3d2ec57f..bbf68657 100644 --- a/sh.err.c +++ b/sh.err.c @@ -188,7 +188,7 @@ extern int enterhist; #define ERR_BADCOLORVAR 134 #define ERR_EOF 135 #define ERR_UNAVAILABLE 136 -#define ERR_DOLFUNC 137 +#define ERR_FUNC 137 #define NO_ERRORS 138 static const char *elst[NO_ERRORS] INIT_ZERO_STRUCT; @@ -368,7 +368,7 @@ 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_DOLFUNC] = CSAVS(1, 140, "Functions are only supported for scripts"); + elst[ERR_FUNC] = CSAVS(1, 140, "Functions are only supported for scripts"); } /* Cleanup data. */ diff --git a/sh.func.c b/sh.func.c index a1b6fc45..b3654359 100644 --- a/sh.func.c +++ b/sh.func.c @@ -2730,7 +2730,7 @@ void dofunction(Char **v, struct command *t) { if (!ffile) - stderror(ERR_DOLFUNC); + stderror(ERR_FUNC); { int i, j; From fc7dbd3955d4bece8a9e020b1de1e0038de7fdff Mon Sep 17 00:00:00 2001 From: Krush206 <37114863+Krush206@users.noreply.github.com> Date: Tue, 21 Nov 2023 18:42:34 -0300 Subject: [PATCH 11/27] sh.func.c: prefer dolzero over ffile --- sh.func.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sh.func.c b/sh.func.c index b3654359..9da6af8b 100644 --- a/sh.func.c +++ b/sh.func.c @@ -2729,7 +2729,7 @@ getYN(const char *prompt) void dofunction(Char **v, struct command *t) { - if (!ffile) + if (!dolzero) stderror(ERR_FUNC); { From d762fba817104ba809c77f5d095e6264bfbb464f Mon Sep 17 00:00:00 2001 From: Krush206 <37114863+Krush206@users.noreply.github.com> Date: Tue, 21 Nov 2023 14:44:21 -0300 Subject: [PATCH 12/27] sh.func.c: allocate memory properly --- sh.func.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sh.func.c b/sh.func.c index 9da6af8b..75e0ea41 100644 --- a/sh.func.c +++ b/sh.func.c @@ -2739,16 +2739,16 @@ dofunction(Char **v, struct command *t) for (i = 0; v[i]; i++) ; - vh = xmalloc((i + 2) * sizeof(Char *)); + vh = xmalloc(sizeof(Char [i + 2])); vh[i + 1] = NULL; for (j = i--; i; i--, j--) { - vh[j] = xmalloc(((Strlen(v[i]) + 1) * sizeof(Char))); + vh[j] = xmalloc(sizeof(Char [Strlen(v[i]) + 1])); Strcpy(vh[j], v[i]); } - vh[1] = xmalloc(Strlen(ffile) + 1); + vh[1] = xmalloc(sizeof (Char [Strlen(ffile) + 1])); Strcpy(vh[1], ffile); - *vh = xmalloc(Strlen(*v) + 1); + *vh = xmalloc(sizeof (Char [Strlen(*v) + 1])); Strcpy(*vh, *v); if (fargv) { From 951264ddcb7d8e8df69538bb0b4a716a7baf387e Mon Sep 17 00:00:00 2001 From: Krush206 <37114863+Krush206@users.noreply.github.com> Date: Wed, 22 Nov 2023 10:35:49 -0300 Subject: [PATCH 13/27] sh.func.c: assign NULL after deallocating fargv --- sh.func.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sh.func.c b/sh.func.c index 75e0ea41..2b13ec41 100644 --- a/sh.func.c +++ b/sh.func.c @@ -2767,7 +2767,9 @@ dofunction(Char **v, struct command *t) if (fargv->prev) { fargv = fargv->prev; free(fargv->next); - } else + } else { free(fargv); + fargv = NULL; + } } } From 7d6eef8e715724e93a680d97beffbdd6a1dc3751 Mon Sep 17 00:00:00 2001 From: Krush206 <37114863+Krush206@users.noreply.github.com> Date: Sun, 3 Dec 2023 16:36:24 -0300 Subject: [PATCH 14/27] Prefer srcfile() over dosource() --- sh.c | 15 +++----------- sh.func.c | 62 +++++++++++++++++++++++++------------------------------ sh.h | 4 ++-- 3 files changed, 33 insertions(+), 48 deletions(-) diff --git a/sh.c b/sh.c index fbe84f46..7215a640 100644 --- a/sh.c +++ b/sh.c @@ -142,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 @@ -1544,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; @@ -1779,7 +1770,7 @@ srcunit(int unit, int onlyown, int hflg, Char **av) Char funcexit[] = { 'e', 'x', 'i', 't', 0 }, funcmain[] = { 'm', 'a', 'i', 'n', 0 }; struct Strbuf aword = Strbuf_INIT; - Sgoal = fargv->v[2]; + Sgoal = fargv->v[0]; Stype = TC_GOTO; fargv->eof = 0; @@ -1817,8 +1808,8 @@ srcunit(int unit, int onlyown, int hflg, Char **av) (void) getword(NULL); } - setq(STRargv, &fargv->v[3], &shvhed, VAR_READWRITE); - dogoto(&fargv->v[1], fargv->t); + setq(STRargv, &fargv->v[1], &shvhed, VAR_READWRITE); + gotolab(fargv->v[0]); { struct Ain a; diff --git a/sh.func.c b/sh.func.c index 2b13ec41..55027d7f 100644 --- a/sh.func.c +++ b/sh.func.c @@ -2732,44 +2732,38 @@ dofunction(Char **v, struct command *t) if (!dolzero) stderror(ERR_FUNC); - { - int i, j; - Char **vh; - - for (i = 0; v[i]; i++) - ; + if (fargv) { + fargv->next = malloc(sizeof *fargv); + fargv->next->prev = fargv; + fargv = fargv->next; + } else { + fargv = malloc(sizeof *fargv); + fargv->prev = NULL; + } - vh = xmalloc(sizeof(Char [i + 2])); - vh[i + 1] = NULL; + { + int i = 0; + Char **vh = 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; + for (v++; *v; v++, i++) { + vh = xrealloc(vh, sizeof(Char *[i + 2])); + vh[i] = xmalloc(sizeof(Char [Strlen(*v) + 1])); + Strcpy(vh[i], *v); } - dosource(fargv->v = vh, fargv->t = t); - /* Reset STRargv on function exit. */ - setv(STRargv, NULL, VAR_READWRITE); + vh[i] = NULL; + fargv->v = vh; + } - if (fargv->prev) { - fargv = fargv->prev; - free(fargv->next); - } else { - free(fargv); - fargv = NULL; - } + 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; } } diff --git a/sh.h b/sh.h index bd06d9bd..f91c7cda 100644 --- a/sh.h +++ b/sh.h @@ -1310,11 +1310,11 @@ extern Char *Sgoal; extern int Stype; extern struct funcargs { Char **v; - struct command *t; + int eof; struct funcargs *prev, *next; - int eof; } *fargv; extern int getword(struct Strbuf *); +extern int srcfile(const char *, int, int, Char **); #endif /* _h_sh */ From c60a4c2ba50ebc0ea0073a622d4260569f9ca892 Mon Sep 17 00:00:00 2001 From: Krush206 <37114863+Krush206@users.noreply.github.com> Date: Wed, 14 Feb 2024 11:39:54 -0300 Subject: [PATCH 15/27] 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.c | 51 ++++++++++++++++++++++++++++++++++++++++++--------- sh.func.c | 38 +++++++++++++++++++++++--------------- sh.h | 22 ++++++++++++++++------ 3 files changed, 81 insertions(+), 30 deletions(-) diff --git a/sh.c b/sh.c index 7215a640..ba547a85 100644 --- a/sh.c +++ b/sh.c @@ -111,7 +111,7 @@ int exitset = 0; static time_t chktim; /* Time mail last checked */ char *progname; int tcsh; -struct funcargs *fargv = NULL; +struct funccurr fcurr; /* * This preserves the input state of the shell. It is used by @@ -1725,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 */ @@ -1766,15 +1767,17 @@ srcunit(int unit, int onlyown, int hflg, Char **av) * 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) { + if (fcurr.ready) { Char funcexit[] = { 'e', 'x', 'i', 't', 0 }, - funcmain[] = { 'm', 'a', 'i', 'n', 0 }; + *funcmain = fcurr.ffile ? + strsave(str2short(fcurr.ffile->file)) : + ffile; struct Strbuf aword = Strbuf_INIT; + Sgoal = fargv->v[0]; Stype = TC_GOTO; - fargv->eof = 0; - - if (!fargv->prev) + fcurr.eof = 0; + if (!(fargv = fcurr.fargv)->prev || fcurr.src) while (1) { (void) getword(&aword); Strbuf_terminate(&aword); @@ -1799,6 +1802,7 @@ srcunit(int unit, int onlyown, int hflg, Char **av) } last = 1; } + fcurr.src = 0; break; } @@ -1807,6 +1811,8 @@ srcunit(int unit, int onlyown, int hflg, Char **av) (void) getword(NULL); } + if (funcmain != ffile) + xfree(funcmain); setq(STRargv, &fargv->v[1], &shvhed, VAR_READWRITE); gotolab(fargv->v[0]); @@ -1826,11 +1832,11 @@ srcunit(int unit, int onlyown, int hflg, Char **av) if (eq(aword.s, funcexit)) { int last = 1, eof = 0; - fargv->eof = 1; + fcurr.eof = 1; while (1) { do { (void) getword(NULL); - if ((intptr_t) getword(&aword) == (intptr_t) &fargv) { + if (getword(&aword) == (1 << 1)) { Strbuf_terminate(&aword); eof = 1; break; @@ -2294,6 +2300,7 @@ dosource(Char **t, struct command *c) Char *f; int hflg = 0; char *file; + struct funcfile **ffile = NULL; USE(c); t++; @@ -2309,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); } diff --git a/sh.func.c b/sh.func.c index 55027d7f..185b994e 100644 --- a/sh.func.c +++ b/sh.func.c @@ -1096,8 +1096,8 @@ getword(struct Strbuf *wp) break; case TC_EXIT: - if (fargv->eof) - return (intptr_t) &fargv; + if (fcurr.eof) + return 1 << 1; setname(short2str(Sgoal)); stderror(ERR_NAME | ERR_NOTFOUND, "exit"); break; @@ -2729,16 +2729,19 @@ getYN(const char *prompt) void dofunction(Char **v, struct command *t) { + char *file; + struct funcargs **fargv; + if (!dolzero) stderror(ERR_FUNC); - if (fargv) { - fargv->next = malloc(sizeof *fargv); - fargv->next->prev = fargv; - fargv = fargv->next; + if (*(fargv = &fcurr.fargv)) { + (*fargv)->next = malloc(sizeof **fargv); + (*fargv)->next->prev = *fargv; + *fargv = (*fargv)->next; } else { - fargv = malloc(sizeof *fargv); - fargv->prev = NULL; + *fargv = malloc(sizeof **fargv); + (*fargv)->prev = NULL; } { @@ -2752,18 +2755,23 @@ dofunction(Char **v, struct command *t) } vh[i] = NULL; - fargv->v = vh; + (*fargv)->v = vh; } + fcurr.ready = 1; - srcfile(short2str(ffile), 0, 0, NULL); + 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); + if ((*fargv)->prev) { + *fargv = (*fargv)->prev; + free((*fargv)->next); } else { - free(fargv); - fargv = NULL; + free(*fargv); + *fargv = NULL; } } diff --git a/sh.h b/sh.h index f91c7cda..3d63fea1 100644 --- a/sh.h +++ b/sh.h @@ -1308,12 +1308,22 @@ extern int filec; /* 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 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 **); From 954cfd6550dccce993d2f99c65729d4466faacba Mon Sep 17 00:00:00 2001 From: Krush206 <37114863+Krush206@users.noreply.github.com> Date: Sat, 18 May 2024 10:24:46 -0300 Subject: [PATCH 16/27] Rethought on functions 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 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 rewinding is possible on unsupported sources. --- sh.c | 191 ++++------------------------------------------------ sh.decls.h | 3 + sh.err.c | 20 +++--- sh.func.c | 162 ++++++++++++++++++++++++++++++++------------ sh.h | 53 +++++++++------ sh.init.c | 3 +- tcsh.man.in | 52 +++++++------- 7 files changed, 204 insertions(+), 280 deletions(-) diff --git a/sh.c b/sh.c index ba547a85..f06561b4 100644 --- a/sh.c +++ b/sh.c @@ -111,47 +111,19 @@ 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 - * 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 **); +#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 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 @@ -1539,7 +1511,11 @@ 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; @@ -1561,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; @@ -1663,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; @@ -1725,7 +1701,6 @@ 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 */ @@ -1761,119 +1736,6 @@ 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 */ @@ -2101,7 +1963,6 @@ process(int catch) getexit(osetexit); omark = cleanup_push_mark(); - for (;;) { struct command *t; int hadhist, old_pintr_disabled; @@ -2286,7 +2147,6 @@ process(int catch) else haderr = 1; } - cleanup_pop_mark(omark); resexit(osetexit); exitset--; @@ -2300,7 +2160,6 @@ dosource(Char **t, struct command *c) Char *f; int hflg = 0; char *file; - struct funcfile **ffile = NULL; USE(c); t++; @@ -2316,39 +2175,13 @@ dosource(Char **t, struct command *c) } f = globone(*t++, G_ERROR); - fcurr.file = file = strsave(short2str(f)); + 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); } diff --git a/sh.decls.h b/sh.decls.h index bd71890d..7e8dc8d3 100644 --- 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 @@ -166,6 +168,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 *); diff --git a/sh.err.c b/sh.err.c index bbf68657..27934e8e 100644 --- a/sh.err.c +++ b/sh.err.c @@ -189,7 +189,10 @@ extern int enterhist; #define ERR_EOF 135 #define ERR_UNAVAILABLE 136 #define ERR_FUNC 137 -#define NO_ERRORS 138 +#define ERR_RETURN 138 +#define ERR_FUNCBEGIN 139 +#define ERR_FUNCALNUM 140 +#define NO_ERRORS 141 static const char *elst[NO_ERRORS] INIT_ZERO_STRUCT; @@ -368,7 +371,10 @@ 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, "Functions are only supported for scripts"); + 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"); } /* Cleanup data. */ @@ -658,13 +664,3 @@ 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))); -} diff --git a/sh.func.c b/sh.func.c index 185b994e..007c59ca 100644 --- a/sh.func.c +++ b/sh.func.c @@ -56,6 +56,7 @@ 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 **); @@ -741,8 +742,8 @@ isrchx(int n) } -int Stype; -Char *Sgoal; +static int Stype; +static Char *Sgoal; static void search(int type, int level, Char *goal) @@ -999,7 +1000,7 @@ histgetword(struct wordent *histent) return NULL; } -int +static int getword(struct Strbuf *wp) { int found = 0, first; @@ -1096,10 +1097,8 @@ getword(struct Strbuf *wp) break; case TC_EXIT: - if (fcurr.eof) - return 1 << 1; setname(short2str(Sgoal)); - stderror(ERR_NAME | ERR_NOTFOUND, "exit"); + stderror(ERR_NAME | ERR_NOTFOUND, "return"); break; default: @@ -2727,51 +2726,128 @@ getYN(const char *prompt) } void -dofunction(Char **v, struct command *t) +dofunction(Char **v, struct command *c) { - char *file; - struct funcargs **fargv; - - if (!dolzero) - stderror(ERR_FUNC); + if (*++v == NULL) { + plist(&functions, VAR_ALL); - if (*(fargv = &fcurr.fargv)) { - (*fargv)->next = malloc(sizeof **fargv); - (*fargv)->next->prev = *fargv; - *fargv = (*fargv)->next; - } else { - *fargv = malloc(sizeof **fargv); - (*fargv)->prev = NULL; + return; } - + Sgoal = *v++; + Stype = TC_EXIT; { - int i = 0; - Char **vh = NULL; + 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))) { + mypipe(pv); + if (!pfork(c, -1)) { + xclose(pv[0]); + while (**varp->vec) + xwrite(pv[1], (*varp->vec)++, 1); + xclose(pv[1]); + xexit(0); + } + pwait(); - for (v++; *v; v++, i++) { - vh = xrealloc(vh, sizeof(Char *[i + 2])); - vh[i] = xmalloc(sizeof(Char [Strlen(*v) + 1])); - Strcpy(vh[i], *v); + xclose(pv[1]); + st_save(&st, pv[0], 0, NULL, v); + process(0); + st_restore(&st); + + 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; + char *prompt = short2str(*c->t_dcom); + + 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, prompt); + (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; + } - vh[i] = NULL; - (*fargv)->v = vh; - } - fcurr.ready = 1; + if (eq(aword.s, funcexit)) { + if (intty) { + ohistent->prev = histgetword(histent); + ohistent->prev->next = ohistent; + savehist(ohistent, 0); + freelex(ohistent); + xfree(ohistent); + } - 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); + 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 ((*fargv)->prev) { - *fargv = (*fargv)->prev; - free((*fargv)->next); - } else { - free(*fargv); - *fargv = NULL; + if (intty) { + ohistent->prev = histgetword(histent); + ohistent->prev->next = ohistent; + savehist(ohistent, 0); + freelex(ohistent); + xfree(ohistent); + } else + (void) getword(NULL); + } + + cleanup_until(&aword); + if (!func.len) + return; + Strbuf_terminate(&func); + **(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); +} diff --git a/sh.h b/sh.h index 3d63fea1..03db8535 100644 --- 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,26 +1335,5 @@ 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 */ diff --git a/sh.init.c b/sh.init.c index 37b09d01..b882b2c2 100644 --- a/sh.init.c +++ b/sh.init.c @@ -80,7 +80,7 @@ const struct biltins bfunc[] = { { "fg", dofg, 0, INF }, { "filetest", dofiletest, 2, INF }, { "foreach", doforeach, 3, INF }, - { "function", dofunction, 1, INF }, + { "function", dofunction, 0, INF }, #ifdef TCF { "getspath", dogetspath, 0, 0 }, { "getxvers", dogetxvers, 0, 0 }, @@ -123,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 */ diff --git a/tcsh.man.in b/tcsh.man.in index efa49a69..92859a69 100644 --- 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,29 +5523,33 @@ 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 -\&... (+) +.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 -.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 \&. +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. . .El .Bl -tag -width 6n From 15d4e30a9efa487c2b1d97950120aa7005f6c3e2 Mon Sep 17 00:00:00 2001 From: Krush206 <37114863+Krush206@users.noreply.github.com> Date: Sun, 2 Jun 2024 12:31:33 -0300 Subject: [PATCH 17/27] Do not fork & fix interrupts Though POSIX defines a minimum for pipe buffer, I decided is safer writing one byte a time. This may render slow reads, though writing in larger quantities rendered broken reads. For an unknown reason, some operations, after SIGINT, rendered exiting the Shell. This happens on the ERR_RECURSION error as well. longjmp remedies the issue, though I'm afraid isn't the best solution. --- sh.err.c | 4 +++- sh.func.c | 42 ++++++++++++++++++++++++++++-------------- sh.h | 3 +++ sh.lex.c | 5 +++++ 4 files changed, 39 insertions(+), 15 deletions(-) diff --git a/sh.err.c b/sh.err.c index 27934e8e..2d795657 100644 --- a/sh.err.c +++ b/sh.err.c @@ -192,7 +192,8 @@ extern int enterhist; #define ERR_RETURN 138 #define ERR_FUNCBEGIN 139 #define ERR_FUNCALNUM 140 -#define NO_ERRORS 141 +#define ERR_RECURSION 141 +#define NO_ERRORS 142 static const char *elst[NO_ERRORS] INIT_ZERO_STRUCT; @@ -375,6 +376,7 @@ errinit(void) 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. */ diff --git a/sh.func.c b/sh.func.c index 007c59ca..b5e2a0b6 100644 --- a/sh.func.c +++ b/sh.func.c @@ -2725,17 +2725,21 @@ getYN(const char *prompt) return doit; } +int fpipe; +Char *fdecl; + void dofunction(Char **v, struct command *c) { if (*++v == NULL) { - plist(&functions, VAR_ALL); + plist(&functions, VAR_READONLY); return; } Sgoal = *v++; Stype = TC_EXIT; { + static int l; int pv[2]; struct saved_state st; struct varent *varp; @@ -2747,20 +2751,31 @@ dofunction(Char **v, struct command *c) if (!alnum(*p)) stderror(ERR_NAME | ERR_FUNCALNUM); if ((varp = adrof1(Sgoal, &functions))) { - mypipe(pv); - if (!pfork(c, -1)) { - xclose(pv[0]); - while (**varp->vec) - xwrite(pv[1], (*varp->vec)++, 1); - xclose(pv[1]); - xexit(0); - } - pwait(); + jmp_buf_t oldexit; + int pvsav, ohaderr; + Char *fsav; - xclose(pv[1]); + if (l == 16) + stderror(ERR_RECURSION); + mypipe(pv); st_save(&st, pv[0], 0, NULL, v); - process(0); + pvsav = fpipe; + fpipe = pv[1]; + fsav = fdecl; + fdecl = *varp->vec; + ohaderr = haderr; + getexit(oldexit); + if (!setexit()) { + l++; + process(0); + } + resexit(oldexit); + haderr = ohaderr; st_restore(&st); + xclose(pv[1]); + fpipe = pvsav; + fdecl = fsav; + l--; return; } @@ -2774,7 +2789,6 @@ dofunction(Char **v, struct command *c) func = Strbuf_INIT; struct wordent *histent = NULL, *ohistent = NULL; - char *prompt = short2str(*c->t_dcom); cleanup_push(&aword, Strbuf_cleanup); while (1) { @@ -2786,7 +2800,7 @@ dofunction(Char **v, struct command *c) histent->prev = ohistent; } if (intty && fseekp == feobp && aret == TCSH_F_SEEK) - printprompt(1, prompt); + printprompt(1, bname); (void) getword(&aword); Strbuf_terminate(&aword); if (intty && Strlen(aword.s) > 0) { diff --git a/sh.h b/sh.h index 03db8535..2875e69f 100644 --- a/sh.h +++ b/sh.h @@ -1335,5 +1335,8 @@ struct saved_state { #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 */ diff --git a/sh.lex.c b/sh.lex.c index 6651e55e..093eff53 100644 --- 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; From 042e84b6b33ff277730a1be93c276eaa56293cd0 Mon Sep 17 00:00:00 2001 From: Krush206 <37114863+Krush206@users.noreply.github.com> Date: Mon, 3 Jun 2024 10:28:27 -0300 Subject: [PATCH 18/27] Refactor --- sh.decls.h | 1 - sh.func.c | 25 +++++++++++-------------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/sh.decls.h b/sh.decls.h index 7e8dc8d3..5a18f99e 100644 --- a/sh.decls.h +++ b/sh.decls.h @@ -107,7 +107,6 @@ 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__)); diff --git a/sh.func.c b/sh.func.c index b5e2a0b6..02503fd5 100644 --- a/sh.func.c +++ b/sh.func.c @@ -2765,10 +2765,9 @@ dofunction(Char **v, struct command *c) fdecl = *varp->vec; ohaderr = haderr; getexit(oldexit); - if (!setexit()) { - l++; + l++; + if (!setexit()) process(0); - } resexit(oldexit); haderr = ohaderr; st_restore(&st); @@ -2810,17 +2809,8 @@ dofunction(Char **v, struct command *c) histent = histent->next; } - if (eq(aword.s, funcexit)) { - if (intty) { - ohistent->prev = histgetword(histent); - ohistent->prev->next = ohistent; - savehist(ohistent, 0); - freelex(ohistent); - xfree(ohistent); - } - + if (eq(aword.s, funcexit)) break; - } Strbuf_append(&func, aword.s); Strbuf_append1(&func, ' '); while (getword(&aword)) { @@ -2846,10 +2836,17 @@ dofunction(Char **v, struct command *c) (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; - Strbuf_terminate(&func); + func.s[--func.len] = 0; **(varvec = xmalloc(sizeof *varvec)) = func.s; *varvec[1] = NULL; setq(Sgoal, *varvec, &functions, VAR_READONLY); From 2fa7967b3927a78b23ee7da93e7d9065ccf5ffca Mon Sep 17 00:00:00 2001 From: Krush206 <37114863+Krush206@users.noreply.github.com> Date: Mon, 3 Jun 2024 11:14:17 -0300 Subject: [PATCH 19/27] tcsh.man.in: correction & additional information --- tcsh.man.in | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tcsh.man.in b/tcsh.man.in index 92859a69..26ad35ff 100644 --- a/tcsh.man.in +++ b/tcsh.man.in @@ -5550,6 +5550,8 @@ The third form calls a function 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 @@ -10780,10 +10782,6 @@ 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. From b950b854f60c02012d635a3540957ce16d0c8275 Mon Sep 17 00:00:00 2001 From: Krush206 <37114863+Krush206@users.noreply.github.com> Date: Mon, 3 Jun 2024 11:43:55 -0300 Subject: [PATCH 20/27] sh.err.c: recursion/nest --- sh.err.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sh.err.c b/sh.err.c index 2d795657..ec15d800 100644 --- a/sh.err.c +++ b/sh.err.c @@ -376,7 +376,7 @@ errinit(void) 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"); + elst[ERR_RECURSION] = CSAVS(1, 144, "Too deep a recursion or nest"); } /* Cleanup data. */ From d840e89e8ec180183d98e6a237b4d646c2e1207e Mon Sep 17 00:00:00 2001 From: Krush206 <37114863+Krush206@users.noreply.github.com> Date: Mon, 3 Jun 2024 18:08:54 -0300 Subject: [PATCH 21/27] Create aliases for functions --- sh.func.c | 14 +++++++++++--- tcsh.man.in | 7 +++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/sh.func.c b/sh.func.c index 02503fd5..f1298b8c 100644 --- a/sh.func.c +++ b/sh.func.c @@ -2783,7 +2783,8 @@ dofunction(Char **v, struct command *c) stderror(ERR_FUNC, Sgoal); { Char funcexit[] = { 'r', 'e', 't', 'u', 'r', 'n', '\0' }, - *(*varvec)[2]; + alarg[] = { '!', '*', '\0' }, + *(*varvec)[4]; struct Strbuf aword = Strbuf_INIT, func = Strbuf_INIT; struct wordent *histent = NULL, @@ -2847,9 +2848,16 @@ dofunction(Char **v, struct command *c) if (!func.len) return; func.s[--func.len] = 0; - **(varvec = xmalloc(sizeof *varvec)) = func.s; - *varvec[1] = NULL; + **(varvec = xcalloc(1, sizeof *varvec - + (sizeof **varvec * 2))) = func.s; setq(Sgoal, *varvec, &functions, VAR_READONLY); + **(varvec = + xcalloc(1, sizeof *varvec)) = Strsave(str2short(bname)); + (*varvec)[1] = Strsave(Sgoal); + (*varvec)[2] = Strsave(alarg); + setq(Sgoal, *varvec, &aliases, VAR_READWRITE); + + tw_cmd_free(); } } } diff --git a/tcsh.man.in b/tcsh.man.in index 26ad35ff..fd8709f4 100644 --- a/tcsh.man.in +++ b/tcsh.man.in @@ -5530,6 +5530,9 @@ loop at the terminal you can rub it out. .It Ic function Ar name Xo .Op Ar arg No ... (+) +.It Ar name Xo +.Op Ar arg No ... +(+) .Xc The first form of the command prints the value of all shell functions. .Pp @@ -5552,6 +5555,10 @@ optionally, preceded by 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. +.Pp +The fourth form is an +.Ic alias +for the third form. . .El .Bl -tag -width 6n From 22d31507658c98c279b01571ac61685d6a4ec4e0 Mon Sep 17 00:00:00 2001 From: Krush206 <37114863+Krush206@users.noreply.github.com> Date: Tue, 4 Jun 2024 12:43:12 -0300 Subject: [PATCH 22/27] sh.func.c: replace setexit with cleanup_push --- sh.func.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/sh.func.c b/sh.func.c index f1298b8c..3c17ecc9 100644 --- a/sh.func.c +++ b/sh.func.c @@ -2739,7 +2739,6 @@ dofunction(Char **v, struct command *c) Sgoal = *v++; Stype = TC_EXIT; { - static int l; int pv[2]; struct saved_state st; struct varent *varp; @@ -2751,26 +2750,24 @@ dofunction(Char **v, struct command *c) if (!alnum(*p)) stderror(ERR_NAME | ERR_FUNCALNUM); if ((varp = adrof1(Sgoal, &functions))) { - jmp_buf_t oldexit; - int pvsav, ohaderr; + static int l; + int pvsav; Char *fsav; - if (l == 16) + if (l == 100) { + l = 0; stderror(ERR_RECURSION); + } mypipe(pv); + cleanup_push(&st, st_restore); 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); + process(0); + cleanup_until(&st); xclose(pv[1]); fpipe = pvsav; fdecl = fsav; @@ -2849,10 +2846,11 @@ dofunction(Char **v, struct command *c) return; func.s[--func.len] = 0; **(varvec = xcalloc(1, sizeof *varvec - - (sizeof **varvec * 2))) = func.s; + (sizeof **varvec * 2))) = + func.s; setq(Sgoal, *varvec, &functions, VAR_READONLY); - **(varvec = - xcalloc(1, sizeof *varvec)) = Strsave(str2short(bname)); + **(varvec = xcalloc(1, sizeof *varvec)) = + Strsave(str2short(bname)); (*varvec)[1] = Strsave(Sgoal); (*varvec)[2] = Strsave(alarg); setq(Sgoal, *varvec, &aliases, VAR_READWRITE); From adea1b664e47700cc6a72a35f5e0efbf5b22f2ae Mon Sep 17 00:00:00 2001 From: Krush206 <37114863+Krush206@users.noreply.github.com> Date: Tue, 4 Jun 2024 12:48:29 -0300 Subject: [PATCH 23/27] Avoid SIGPIPE on doexit --- sh.c | 10 ++++++++-- sh.func.c | 17 ++++++++++++----- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/sh.c b/sh.c index f06561b4..816187a5 100644 --- a/sh.c +++ b/sh.c @@ -1627,12 +1627,18 @@ st_save(struct saved_state *st, int unit, int hflg, Char **al, Char **av) gointr = 0; evalvec = 0; evalp = 0; - alvec = al; alvecp = 0; enterhist = hflg; if (enterhist) HIST = '\0'; - insource = 1; + if (al == &fdecl) { + alvec = NULL; + insource = 2; + } + else { + alvec = al; + insource = 1; + } } diff --git a/sh.func.c b/sh.func.c index 3c17ecc9..abb2ec12 100644 --- a/sh.func.c +++ b/sh.func.c @@ -498,11 +498,18 @@ doexit(Char **v, struct command *c) stderror(ERR_NAME | ERR_EXPRESSION); } btoeof(); -#if 0 - if (intty) -#endif - /* Always close, why only on ttys? */ + + /* Always close, except in the context of + * dosource or dofunction. + * st_restore will handle. */ + switch (insource) { + case 0: xclose(SHIN); + SHIN = -1; + break; + case 2: + fdecl += Strlen(fdecl); + } } /*ARGSUSED*/ @@ -2760,7 +2767,7 @@ dofunction(Char **v, struct command *c) } mypipe(pv); cleanup_push(&st, st_restore); - st_save(&st, pv[0], 0, NULL, v); + st_save(&st, pv[0], 0, &fdecl, v); pvsav = fpipe; fpipe = pv[1]; fsav = fdecl; From d0167d4a55a24b8b3b0fa089a6466ba5e1b30fa6 Mon Sep 17 00:00:00 2001 From: Krush206 <37114863+Krush206@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:56:45 -0300 Subject: [PATCH 24/27] sh.func.c: don't catch OLDSTD --- sh.func.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sh.func.c b/sh.func.c index abb2ec12..52389888 100644 --- a/sh.func.c +++ b/sh.func.c @@ -2782,8 +2782,8 @@ dofunction(Char **v, struct command *c) return; } - if (*v || c->t_dflg & (F_PIPEIN | F_PIPEOUT) || - c->t_dlef || c->t_drit || !isatty(OLDSTD)) + if (*v || c->t_dlef || c->t_drit || + c->t_dflg & (F_PIPEIN | F_PIPEOUT)) stderror(ERR_FUNC, Sgoal); { Char funcexit[] = { 'r', 'e', 't', 'u', 'r', 'n', '\0' }, From 911ed6ebd695e6c593e162db3814d07271bdc060 Mon Sep 17 00:00:00 2001 From: Krush206 <37114863+Krush206@users.noreply.github.com> Date: Thu, 6 Jun 2024 10:43:22 -0300 Subject: [PATCH 25/27] 22d3150: move remaining parts to st_restore --- sh.c | 5 +++++ sh.func.c | 13 ++++--------- sh.h | 2 ++ sh.lex.c | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/sh.c b/sh.c index 816187a5..f3ea8354 100644 --- a/sh.c +++ b/sh.c @@ -1670,6 +1670,11 @@ st_restore(void *xst) xclose(SHIN); + if (insource == 2) { + xclose(fpipe); + fpipe = st->fpipe; + fdecl = st->fdecl; + } insource = st->insource; SHIN = st->SHIN; if (st->OLDSTD != -1) diff --git a/sh.func.c b/sh.func.c index 52389888..1eed8e68 100644 --- a/sh.func.c +++ b/sh.func.c @@ -2732,7 +2732,7 @@ getYN(const char *prompt) return doit; } -int fpipe; +int fpipe = -1; Char *fdecl; void @@ -2758,8 +2758,6 @@ dofunction(Char **v, struct command *c) stderror(ERR_NAME | ERR_FUNCALNUM); if ((varp = adrof1(Sgoal, &functions))) { static int l; - int pvsav; - Char *fsav; if (l == 100) { l = 0; @@ -2768,16 +2766,13 @@ dofunction(Char **v, struct command *c) mypipe(pv); cleanup_push(&st, st_restore); st_save(&st, pv[0], 0, &fdecl, v); - pvsav = fpipe; + st.fpipe = fpipe; fpipe = pv[1]; - fsav = fdecl; + st.fdecl = fdecl; fdecl = *varp->vec; l++; process(0); cleanup_until(&st); - xclose(pv[1]); - fpipe = pvsav; - fdecl = fsav; l--; return; @@ -2851,7 +2846,7 @@ dofunction(Char **v, struct command *c) cleanup_until(&aword); if (!func.len) return; - func.s[--func.len] = 0; + Strbuf_terminate(&func); **(varvec = xcalloc(1, sizeof *varvec - (sizeof **varvec * 2))) = func.s; diff --git a/sh.h b/sh.h index 2875e69f..a083d76c 100644 --- a/sh.h +++ b/sh.h @@ -1294,6 +1294,8 @@ struct saved_state { int cantell; struct Bin B; int justpr; + int fpipe; + Char *fdecl; }; #include "sh.decls.h" diff --git a/sh.lex.c b/sh.lex.c index 093eff53..0c682dcc 100644 --- a/sh.lex.c +++ b/sh.lex.c @@ -1717,7 +1717,7 @@ bgetc(void) buf = (int) feobp / BUFSIZE; balloc(buf); roomleft = BUFSIZE - off; - if (fpipe) { + if (insource == 2) { if (!*fdecl) return CHAR_ERR; (void) xwrite(fpipe, fdecl++, (size_t) 1); From 56e5e7d03636a92bbc071cff780cc05c157eb67b Mon Sep 17 00:00:00 2001 From: Krush206 <37114863+Krush206@users.noreply.github.com> Date: Fri, 7 Jun 2024 13:24:28 -0300 Subject: [PATCH 26/27] 911ed6e: more remaining parts --- sh.c | 4 ++++ sh.func.c | 13 +++---------- sh.h | 4 +++- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/sh.c b/sh.c index f3ea8354..1b73a600 100644 --- a/sh.c +++ b/sh.c @@ -1634,6 +1634,9 @@ st_save(struct saved_state *st, int unit, int hflg, Char **al, Char **av) if (al == &fdecl) { alvec = NULL; insource = 2; + st->fpipe = fpipe; + st->fdecl = *al; + flvl++; } else { alvec = al; @@ -1674,6 +1677,7 @@ st_restore(void *xst) xclose(fpipe); fpipe = st->fpipe; fdecl = st->fdecl; + flvl--; } insource = st->insource; SHIN = st->SHIN; diff --git a/sh.func.c b/sh.func.c index 1eed8e68..b2e678ab 100644 --- a/sh.func.c +++ b/sh.func.c @@ -2732,7 +2732,8 @@ getYN(const char *prompt) return doit; } -int fpipe = -1; +int flvl, + fpipe = -1; Char *fdecl; void @@ -2757,23 +2758,15 @@ dofunction(Char **v, struct command *c) if (!alnum(*p)) stderror(ERR_NAME | ERR_FUNCALNUM); if ((varp = adrof1(Sgoal, &functions))) { - static int l; - - if (l == 100) { - l = 0; + if (flvl == 100) stderror(ERR_RECURSION); - } mypipe(pv); cleanup_push(&st, st_restore); st_save(&st, pv[0], 0, &fdecl, v); - st.fpipe = fpipe; fpipe = pv[1]; - st.fdecl = fdecl; fdecl = *varp->vec; - l++; process(0); cleanup_until(&st); - l--; return; } diff --git a/sh.h b/sh.h index a083d76c..721e5192 100644 --- a/sh.h +++ b/sh.h @@ -1337,7 +1337,9 @@ struct saved_state { #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 int flvl, /* Level of nesting or recursion used + * used by dofunction. */ + fpipe; /* Write end of a pipe used by dofunction. */ extern Char *fdecl; /* Pointer to function declaration * used by dofunction. */ From c8b8f512f2c5b552ec4f89189b56bda7db8d982d Mon Sep 17 00:00:00 2001 From: Krush206 <37114863+Krush206@users.noreply.github.com> Date: Thu, 13 Jun 2024 18:08:43 -0300 Subject: [PATCH 27/27] Prefer dozip over doreturn --- sh.err.c | 14 ++++++-------- sh.func.c | 11 +---------- sh.init.c | 2 +- 3 files changed, 8 insertions(+), 19 deletions(-) diff --git a/sh.err.c b/sh.err.c index ec15d800..6b95f465 100644 --- a/sh.err.c +++ b/sh.err.c @@ -188,12 +188,11 @@ extern int enterhist; #define ERR_BADCOLORVAR 134 #define ERR_EOF 135 #define ERR_UNAVAILABLE 136 -#define ERR_FUNC 137 -#define ERR_RETURN 138 -#define ERR_FUNCBEGIN 139 -#define ERR_FUNCALNUM 140 -#define ERR_RECURSION 141 -#define NO_ERRORS 142 +#define ERR_UNDFUNC 137 +#define ERR_FUNCBEGIN 138 +#define ERR_FUNCALNUM 139 +#define ERR_RECURSION 140 +#define NO_ERRORS 141 static const char *elst[NO_ERRORS] INIT_ZERO_STRUCT; @@ -372,8 +371,7 @@ 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_UNDFUNC] = CSAVS(1, 140, "%S: Undeclared function"); 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, "Too deep a recursion or nest"); diff --git a/sh.func.c b/sh.func.c index b2e678ab..a9862024 100644 --- a/sh.func.c +++ b/sh.func.c @@ -2772,7 +2772,7 @@ dofunction(Char **v, struct command *c) } if (*v || c->t_dlef || c->t_drit || c->t_dflg & (F_PIPEIN | F_PIPEOUT)) - stderror(ERR_FUNC, Sgoal); + stderror(ERR_UNDFUNC, Sgoal); { Char funcexit[] = { 'r', 'e', 't', 'u', 'r', 'n', '\0' }, alarg[] = { '!', '*', '\0' }, @@ -2854,12 +2854,3 @@ dofunction(Char **v, struct command *c) } } } - -void -doreturn(Char **v, struct command *c) -{ - USE(c); - USE(v); - - stderror(ERR_NAME | ERR_RETURN); -} diff --git a/sh.init.c b/sh.init.c index b882b2c2..d751b754 100644 --- a/sh.init.c +++ b/sh.init.c @@ -123,7 +123,7 @@ const struct biltins bfunc[] = { { "pushd", dopushd, 0, INF }, { "rehash", dohash, 0, 3 }, { "repeat", dorepeat, 2, INF }, - { "return", doreturn, 0, 0 }, + { "return", dozip, 0, 0 }, #ifdef apollo { "rootnode", dorootnode, 1, 1 }, #endif /* apollo */