text: /*
   *	expfunc.c
   *
   *	function expander for the obsolete /bin/sh
   */
  
  #include "headers.h"
  #include <ctype.h>
  
  #define	MAXLINEBUF		255
  #define	MAXFUNCNO		32
  #define	MAXARGS			10
  
  extern VOID exit __P_((int));
  
  static char *NEAR skipspace __P_((char *));
  static char *NEAR Xstrndup __P_((CONST char *, int));
  static char *NEAR isfunction __P_((char *));
  static int NEAR entryfunc __P_((char *));
  static char *NEAR checkhere __P_((char *));
  static int NEAR getargs __P_((char **, char *[]));
  static char *NEAR expargs __P_((CONST char *, char *[]));
  static int NEAR searchfunc __P_((CONST char *));
  static int NEAR evalline __P_((char *, char *[]));
  static char *NEAR getline __P_((FILE *));
  int main __P_((int, char *CONST []));
  
  static int funcno = 0;
  static char *func[MAXFUNCNO];
  static int funclen[MAXFUNCNO];
  static char *funcbody[MAXFUNCNO];
  
  
  static char *NEAR skipspace(s)
  char *s;
  {
  	while (*s == ' ' || *s == '\t') s++;
  
  	return(s);
  }
  
  static char *NEAR Xstrndup(s, n)
  CONST char *s;
  int n;
  {
  	char *tmp;
  
  	if (!s) return(NULL);
  	if (!(tmp = (char *)malloc(n + 1))) exit(1);
  	memcpy(tmp, s, n);
  	tmp[n] = '\0';
  
  	return(tmp);
  }
  
  static char *NEAR isfunction(line)
  char *line;
  {
  	char *cp;
  	int len;
  
  	if (funcno >= MAXFUNCNO) return(NULL);
  
  	line = skipspace(line);
  
  	for (len = 0; line[len]; len++)
  		if (line[len] != '_' && !isalpha((int)(line[len]))) break;
  	if (!len) return(NULL);
  
  	cp = skipspace(&(line[len]));
  	if (*cp != '(') return(NULL);
  
  	cp = skipspace(&(cp[1]));
  	if (*cp != ')') return(NULL);
  
  	cp = skipspace(&(cp[1]));
  	if (*cp != '{') return(NULL);
  
  	func[funcno] = Xstrndup(line, len);
  	funclen[funcno] = len;
  	funcbody[funcno] = NULL;
  
  	return(&(cp[1]));
  }
  
  static int NEAR entryfunc(line)
  char *line;
  {
  	char *cp;
  	int i, len, quote;
  
  	i = 1;
  	quote = '\0';
  	if (*line == '}') {
  		i = 0;
  		line = "";
  	}
  	else for (cp = line; *cp; cp++) {
  		if (*cp == quote) quote = '\0';
  		else if (quote == '\'') continue;
  		else if (*cp == '\\' && cp[1]) cp++;
  		else if (quote) continue;
  		else if (*cp == '\'' || *cp == '"') quote = *cp;
  		else if (*cp == '}') {
  			*cp = '\0';
  			i = 0;
  		}
  	}
  
  	len = strlen(line);
  	if (!i) len += 3;
  	if (!funcbody[funcno]) {
  		funcbody[funcno] = (char *)malloc(len + 2 + 1);
  		if (!funcbody[funcno]) exit(1);
  		strcpy(funcbody[funcno], "{ ");
  	}
  	else {
  		len += strlen(funcbody[funcno]);
  		funcbody[funcno] = (char *)realloc(funcbody[funcno], len + 2);
  		if (!funcbody[funcno]) exit(1);
  		if (*line) strcat(funcbody[funcno], "\n");
  	}
  
  	strcat(funcbody[funcno], line);
  	if (!i) strcat(funcbody[funcno++], "; }");
  
  	return(i);
  }
  
  static char *NEAR checkhere(line)
  char *line;
  {
  	char *cp;
  	int len, quote;
  
  	quote = '\0';
  	for (cp = line; *cp; cp++) {
  		if (*cp == quote) quote = '\0';
  		else if (quote == '\'') continue;
  		else if (*cp == '\\' && cp[1]) cp++;
  		else if (quote) continue;
  		else if (*cp == '\'' || *cp == '"') quote = *cp;
  		else if (*cp == '<' && cp[1] == '<') break;
  	}
  	if (*cp != '<') return(NULL);
  
  	cp = skipspace(&(cp[2]));
  	for (len = 0; cp[len]; len++)
  		if (cp[len] == ' ' || cp[len] == '\t') break;
  
  	return(Xstrndup(cp, len));
  }
  
  static int NEAR getargs(linep, args)
  char **linep, *args[];
  {
  	char *cp, *new, *line, *buf;
  	int i, n, len, quote, size;
  
  	for (i = 0; i < MAXARGS; i++) args[i] = NULL;
  
  	line = *linep;
  	for (i = 0; i < MAXARGS; i++) {
  		new = buf = NULL;
  		n = size = 0;
  		cp = line = skipspace(line);
  		quote = '\0';
  		for (;;) {
  			if (!*cp) {
  				if (!quote) break;
  
  				len = strlen(line);
  				if (!buf) buf = (char *)malloc(len + 1);
  				else buf = (char *)realloc(buf,
  					size + 1 + len + 1);
  				if (!buf) exit(1);
  
  				if (size) {
  					buf[size++] = '\n';
  					n++;
  				}
  				memcpy(&(buf[size]), line, len + 1);
  				size += len;
  
  				cp = line = getline(stdin);
  				if (!cp) break;
  				continue;
  			}
  
  			if (*cp == quote) quote = '\0';
  			else if (quote == '\'') /*EMPTY*/;
  			else if (*cp == '\\') {
  				if (cp[1] == '\n') break;
  				else if (cp[1]) {
  					cp++;
  					n++;
  				}
  				else {
  					new = getline(stdin);
  					if (!new || line > cp) break;
  					cp = line = new;
  					new = NULL;
  					continue;
  				}
  			}
  			else if (quote) /*EMPTY*/;
  			else if (*cp == '\'' || *cp == '"') quote = *cp;
  			else if (strchr(" \t\n!&);<>`|}", *cp)) break;
  
  			cp++;
  			n++;
  		}
  
  		if (n) {
  			if (buf) {
  				len = strlen(line);
  				buf = (char *)realloc(buf, size + 1 + len + 1);
  				if (!buf) exit(1);
  				buf[size++] = '\n';
  				memcpy(&(buf[size]), line, len + 1);
  				size += len;
  				line = buf;
  			}
  
  			if (*line == '\'' || *line == '"') {
  				n--;
  				if (*(cp - 1) == *line) n--;
  				line++;
  			}
  			args[i] = Xstrndup(line, n);
  		}
  		if (buf) free(buf);
  
  		if (new) line = new;
  		else if (*cp == '\\' && cp[1] == '\n') line = &(cp[2]);
  		else if (*cp == ' ' || *cp == '\t') line = cp;
  		else {
  			i++;
  			break;
  		}
  	}
  	*linep = cp;
  
  	return(i);
  }
  
  static char *NEAR expargs(line, args)
  CONST char *line;
  char *args[];
  {
  	char *cp, *buf, *tmp, *top;
  	int i, len, alen, vlen, rlen, quote;
  
  	len = strlen(line);
  	buf = Xstrndup(line, len);
  
  	quote = '\0';
  	cp = buf;
  	while (*cp) {
  		if (*cp == quote) quote = '\0';
  		else if (quote == '\'') /*EMPTY*/;
  		else if (*cp == '\\' && cp[1]) cp++;
  		else if (*cp == '$') {
  			top = cp;
  			if (*(++cp) == '{') cp++;
  			if (*cp >= '1' && *cp <= '9') {
  				i = *cp - '1';
  				if (*(++cp) == '}') cp++;
  
  				alen = cp - top;
  				rlen = len - (cp - buf);
  				vlen = (args[i]) ? strlen(args[i]) : 0;
  				len = len - alen + vlen;
  
  				if (vlen > alen) {
  					tmp = (char *)realloc(buf, len + 1);
  					if (!tmp) exit(1);
  					cp = &(tmp[cp - buf]);
  					top = &(tmp[top - buf]);
  					buf = tmp;
  				}
  
  				memmove(&(top[vlen]), cp, rlen + 1);
  				if (vlen) memcpy(top, args[i], vlen);
  				continue;
  			}
  		}
  		else if (quote) /*EMPTY*/;
  		else if (*cp == '\'' || *cp == '"') quote = *cp;
  
  		cp++;
  	}
  
  	return(buf);
  }
  
  static int NEAR searchfunc(s)
  CONST char *s;
  {
  	int i, len;
  
  	for (i = 0; i < funcno; i++) {
  		len = funclen[i];
  		if (!strncmp(s, func[i], len))
  			if (s[len] == ' ' || s[len] == '\t') return(i);
  	}
  
  	return(-1);
  }
  
  static int NEAR evalline(line, args)
  char *line, *args[];
  {
  	char *cp, *newargs[MAXARGS];
  	int i, quote;
  
  	if (args) line = expargs(line, args);
  
  	quote = '\0';
  	for (cp = line; *cp; cp++) {
  		if (*cp == quote) quote = '\0';
  		else if (quote) /*EMPTY*/;
  		else if (*cp == '\'' || *cp == '"') quote = *cp;
  		else if ((i = searchfunc(cp)) >= 0) {
  			cp += funclen[i];
  			getargs(&cp, newargs);
  			evalline(funcbody[i], newargs);
  			for (i = 0; i < MAXARGS; i++)
  				if (newargs[i]) free(newargs[i]);
  			cp--;
  			continue;
  		}
  
  		fputc(*cp, stdout);
  	}
  
  	i = strlen(line);
  	if (args) free(line);
  	else fputc('\n', stdout);
  
  	return(i);
  }
  
  static char *NEAR getline(fp)
  FILE *fp;
  {
  	static char buf[MAXLINEBUF + 1];
  	int len;
  
  	for (;;) {
  		*buf = '\0';
  		if (!fgets(buf, MAXLINEBUF, fp)) return(NULL);
  		len = strlen(buf);
  		if (len > 0 && buf[--len] == '\n') buf[len] = '\0';
  		if (*(skipspace(buf)) != '#') break;
  		fprintf(stdout, "%s\n", buf);
  	}
  
  	return(buf);
  }
  
  /*ARGSUSED*/
  int main(argc, argv)
  int argc;
  char *CONST argv[];
  {
  	char *cp, *heredoc, *line;
  	int infunc;
  
  	infunc = 0;
  	heredoc = NULL;
  	func[0] = "return";
  	funclen[0] = strlen(func[0]);
  	funcbody[0] = "(exit $1)";
  	funcno = 1;
  
  	while ((line = getline(stdin))) {
  		if (heredoc) {
  			fprintf(stdout, "%s\n", line);
  			if (!strcmp(line, heredoc)) {
  				free(heredoc);
  				heredoc = NULL;
  			}
  		}
  		else {
  			heredoc = checkhere(line);
  			if (infunc) infunc = entryfunc(line);
  			else if ((cp = isfunction(line)))
  				infunc = (*cp) ? entryfunc(cp) : 1;
  			else evalline(line, NULL);
  		}
  	}
  
  	return(0);
  }
  