Skip to content

Commit

Permalink
Introduce 'function' built-in
Browse files Browse the repository at this point in the history
As requested in #4, here's my proposal. This is a wrapper around goto and source. The script recurses itself and searches for a goto label. It's an error for labels not contain an exit to their end. Function calls outside labels are, by default, labeled main.

This was tested sparsely, and may contain bugs I haven't faced, but is working as expected. One bug to be noted is that pipes don't give up on errors. This is possibly due to forking.

I noticed Tcsh has a built-in function command, but I can't trace the code. Said built-in function command is evaluated before mine, thus those who attempt to execute it won't get the correct error message.
  • Loading branch information
Krush206 committed Jul 7, 2023
1 parent e271bbd commit 3dca78b
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 5 deletions.
68 changes: 68 additions & 0 deletions sh.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -1993,6 +1994,59 @@ process(int catch)

getexit(osetexit);
omark = cleanup_push_mark();

/* If this is a function, setup STRargv and invoke goto. */
if (fargv) {
int funcdelim = 0;
Char funcexit[] = { 'e', 'x', 'i', 't', 0 },
funcmain[] = { 'm', 'a', 'i', 'n', 0 };
struct Strbuf aword = Strbuf_INIT;
cleanup_push(&aword, Strbuf_cleanup);
Sgoal = funcmain;
Stype = TC_GOTO;

if (!fargv->prev)
while (!funcdelim) {
(void) getword(&aword);
Strbuf_terminate(&aword);

if (aword.s[0] != ':' && lastchr(aword.s) == ':')
funcerror(funcmain, funcexit);
else if (eq(aword.s, funcexit))
funcdelim = 1;

(void) getword(NULL);
}

setq(STRargv, &fargv->v[3], &shvhed, 0);
dogoto(&fargv->v[1], fargv->t);

{
struct Ain a;

cleanup_push(&aword, Strbuf_cleanup);
Sgoal = fargv->v[2];
Stype = TC_EXIT;
a.type = TCSH_F_SEEK;
btell(&a);
funcdelim = 0;

while (!funcdelim) {
(void) getword(&aword);
Strbuf_terminate(&aword);

if (aword.s[0] != ':' && lastchr(aword.s) == ':')
funcerror(fargv->v[2], funcexit);
else if (eq(aword.s, funcexit))
funcdelim = 1;

(void) getword(NULL);
}

bseek(&a);
}
}

for (;;) {
struct command *t;
int hadhist, old_pintr_disabled;
Expand Down Expand Up @@ -2177,6 +2231,20 @@ process(int catch)
else
haderr = 1;
}

if (fargv) {
/* Reset STRargv on function exit. */
setv(STRargv, NULL, 0);

if (fargv->prev)
{
fargv = fargv->prev;
free(fargv->next);
}
else
free(fargv);
}

cleanup_pop_mark(omark);
resexit(osetexit);
exitset--;
Expand Down
2 changes: 2 additions & 0 deletions sh.decls.h
Original file line number Diff line number Diff line change
Expand Up @@ -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__));

Expand Down Expand Up @@ -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 *);
Expand Down
17 changes: 16 additions & 1 deletion sh.err.c
Original file line number Diff line number Diff line change
Expand Up @@ -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_DOLFUNC 136
#define NO_ERRORS 137

static const char *elst[NO_ERRORS] INIT_ZERO_STRUCT;

Expand Down Expand Up @@ -365,6 +366,7 @@ errinit(void)
elst[ERR_BADJOB] = CSAVS(1, 136, "No such job (badjob)");
elst[ERR_BADCOLORVAR] = CSAVS(1, 137, "Unknown colorls variable '%c%c'");
elst[ERR_EOF] = CSAVS(1, 138, "Unexpected end of file");
elst[ERR_DOLFUNC] = CSAVS(1, 139, "Functions are only supported for scripts");
}

/* Cleanup data. */
Expand Down Expand Up @@ -654,3 +656,16 @@ stderror(unsigned int id, ...)

reset(); /* Unwind */
}

void
funcerror(Char *n, Char *msg)
{
char nconv[Strlen(n) + 1],
msgconv[Strlen(msg) + 1];

strcpy(nconv, short2str(n));
strcpy(msgconv, short2str(msg));

setname(nconv);
stderror(ERR_NAME | ERR_NOTFOUND, msgconv);
}
50 changes: 46 additions & 4 deletions sh.func.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 **);
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -1000,7 +999,7 @@ histgetword(struct wordent *histent)
return NULL;
}

static int
int
getword(struct Strbuf *wp)
{
int found = 0, first;
Expand Down Expand Up @@ -1096,6 +1095,11 @@ getword(struct Strbuf *wp)
stderror(ERR_NAME | ERR_NOTFOUND, "label");
break;

case TC_EXIT:
setname(short2str(Sgoal));
stderror(ERR_NAME | ERR_NOTFOUND, "exit");
break;

default:
break;
}
Expand Down Expand Up @@ -2694,3 +2698,41 @@ getYN(const char *prompt)
continue;
return doit;
}

void
dofunction(Char **v, struct command *t)
{
if (!ffile)
stderror(ERR_DOLFUNC);

{
int i, j;
Char **vh;

for (i = 0; v[i]; i++)
;

vh = xmalloc((i + 2) * sizeof(Char *));
vh[i + 1] = NULL;

for (j = i--; i; i--, j--) {
vh[j] = xmalloc(((Strlen(v[i]) + 1) * sizeof(Char)));
Strcpy(vh[j], v[i]);
}
vh[1] = xmalloc(Strlen(ffile) + 1);
Strcpy(vh[1], ffile);
*vh = xmalloc(Strlen(*v) + 1);
Strcpy(*vh, *v);

if (fargv) {
fargv->next = malloc(sizeof *fargv);
fargv->next->prev = fargv;
fargv = fargv->next;
} else {
fargv = malloc(sizeof *fargv);
fargv->prev = NULL;
}

dosource(fargv->v = vh, fargv->t = t);
}
}
10 changes: 10 additions & 0 deletions sh.h
Original file line number Diff line number Diff line change
Expand Up @@ -1302,5 +1302,15 @@ extern int filec;
#define TEXP_IGNORE 1 /* in ignore, it means to ignore value, just parse */
#define TEXP_NOGLOB 2 /* in ignore, it means not to globone */

/* Function variable(s) and function(s). */
extern Char *Sgoal;
extern int Stype;
extern struct funcargs {
Char **v;
struct command *t;
struct funcargs *prev,
*next;
} *fargv;
extern int getword(struct Strbuf *);

#endif /* _h_sh */
1 change: 1 addition & 0 deletions sh.init.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down

0 comments on commit 3dca78b

Please sign in to comment.