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 | 
