View Issue Details

IDProjectCategoryView StatusLast Update
0000490tcshGeneralpublic2024-02-14 14:57
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)   
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)   
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)   

MProG10

2023-12-03 19:44

reporter   ~0003985

Prefer srcfile() over dosource()
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)   
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.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)   

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.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)   
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)   

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