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 |