View Issue Details

IDProjectCategoryView StatusLast Update
0000490tcshGeneralpublic2024-06-03 14:49
ReporterMProG10 Assigned To 
PrioritynormalSeverityfeatureReproducibilityN/A
Status newResolutionopen 
Summary0000490: Introduce 'function' built-in.
DescriptionThis 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 Informationhttps://github.com/tcsh-org/tcsh/pull/77
TagsNo tags attached.

Activities

MProG10

2023-12-02 00:49

reporter   ~0003981

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 */
sh.h.diff (486 bytes)   
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.decls.h.diff (772 bytes)   
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.c.diff (3,194 bytes)   
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.err.c.diff (929 bytes)   
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.func.c.diff (1,931 bytes)   

MProG10

2023-12-02 03:03

reporter   ~0003982

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.
tcsh.man.in.diff (1,096 bytes)   
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	},
sh.init.c.diff (325 bytes)   
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()
commands.at.diff (1,110 bytes)   

MProG10

2023-12-03 19:44

reporter   ~0003985

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.c-2.diff (3,818 bytes)   
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;
+    }
+}
sh.func.c-2.diff (1,765 bytes)   
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.h-2.diff (516 bytes)   

MProG10

2024-02-14 14:57

reporter   ~0004011

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.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;
+    }
+}
sh.func.c-3.diff (2,081 bytes)   
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.h-3.diff (720 bytes)   
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.c-3.diff (5,424 bytes)   

MProG10

2024-06-03 14:34

reporter   ~0004052

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.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.init.c-2.diff (599 bytes)   
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.h-4.diff (1,500 bytes)   
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
tcsh.man.in-2.diff (1,354 bytes)   
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.c-4.diff (1,636 bytes)   
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.decls.h-2.diff (1,243 bytes)   
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;
sh.lex.c.diff (353 bytes)   
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.err.c-2.diff (1,054 bytes)   
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);
+}
sh.func.c-4.diff (3,563 bytes)   

MProG10

2024-06-03 14:49

reporter   ~0004053

Recursion/nest.

Issue History

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