Subject: /bin/sh patch
To: None <current-users@NetBSD.ORG>
From: Christos Zoulas <christos@deshaw.com>
List: current-users
Date: 01/12/1995 23:18:51
To support:

    ${VAR#PAT}
    ${VAR##PAT}
    ${VAR%PAT}
    ${VAR%%PAT}
    ${#VAR}

I have not tested this much, so use at own risk.
Please let me know if you find any problems...

christos


*** 1.1	1995/01/13 04:12:37
--- expand.c	1995/01/13 04:10:26
***************
*** 59,64 ****
--- 59,65 ----
  #include "memalloc.h"
  #include "error.h"
  #include "mystring.h"
+ #include "extern.h"
  #include <sys/types.h>
  #include <sys/time.h>
  #include <sys/stat.h>
***************
*** 443,452 ****
--- 444,456 ----
  	int varflags;
  	char *var;
  	char *val;
+ 	char *pat;
  	int c;
  	int set;
  	int special;
  	int startloc;
+ 	int varlen;
+ 	int easy;
  	int quotes = flag & (EXP_FULL | EXP_CASE);
  
  	varflags = *p++;
***************
*** 468,498 ****
  		} else
  			set = 1;
  	}
  	startloc = expdest - stackblock();
  	if (set && subtype != VSPLUS) {
  		/* insert the value of the variable */
  		if (special) {
  			varvalue(*var, varflags & VSQUOTE, flag & EXP_FULL);
  		} else {
  			char const *syntax = (varflags & VSQUOTE)? DQSYNTAX : BASESYNTAX;
  
! 			while (*val) {
! 				if (quotes && syntax[*val] == CCTL)
! 					STPUTC(CTLESC, expdest);
! 				STPUTC(*val++, expdest);
  			}
  		}
  	}
  	if (subtype == VSPLUS)
  		set = ! set;
! 	if (((varflags & VSQUOTE) == 0 || (*var == '@' && shellparam.nparam != 1))
! 	 && (set || subtype == VSNORMAL))
! 		recordregion(startloc, expdest - stackblock(), varflags & VSQUOTE);
! 	if (! set && subtype != VSNORMAL) {
! 		if (subtype == VSPLUS || subtype == VSMINUS) {
! 			argstr(p, flag);
! 		} else {
  			char *startp;
  			int saveherefd = herefd;
  			struct nodelist *saveargbackq = argbackq;
  			herefd = -1;
--- 472,552 ----
  		} else
  			set = 1;
  	}
+ 	varlen = 0;
  	startloc = expdest - stackblock();
  	if (set && subtype != VSPLUS) {
  		/* insert the value of the variable */
  		if (special) {
+ 			char *exp, *oexpdest = expdest;
  			varvalue(*var, varflags & VSQUOTE, flag & EXP_FULL);
+ 			if (subtype == VSLENGTH) {
+ 				for (exp = oexpdest;exp != expdest; exp++)
+ 					varlen++;
+ 				expdest = oexpdest;
+ 			}
  		} else {
  			char const *syntax = (varflags & VSQUOTE)? DQSYNTAX : BASESYNTAX;
  
! 			if (subtype == VSLENGTH) {
! 				for (;*val; val++)
! 					varlen++;
! 			}
! 			else {
! 				while (*val) {
! 					if (quotes && syntax[*val] == CCTL)
! 						STPUTC(CTLESC, expdest);
! 					STPUTC(*val++, expdest);
! 				}
! 
  			}
  		}
  	}
+ 		
  	if (subtype == VSPLUS)
  		set = ! set;
! 
! 	easy = ((varflags & VSQUOTE) == 0 || 
! 		(*var == '@' && shellparam.nparam != 1)) && set;
! 
! 
! 	switch (subtype) {
! 	case VSLENGTH:
! 		expdest = cvtnum(varlen, expdest);
! 		/* FALLTHROUGH */
! 
! 	case VSNORMAL:
! record:
! 		recordregion(startloc, expdest - stackblock(), 
! 			     varflags & VSQUOTE);
! 		break;
! 
! 	case VSPLUS:
! 	case VSMINUS:
! 		if (easy)
! 			goto record;
! 		argstr(p, flag);
! 		break;
! 
! 	case VSTRIMLEFT:
! 	case VSTRIMLEFTMAX:
! 	case VSTRIMRIGHT:
! 	case VSTRIMRIGHTMAX:
! 		STPUTC('\0', expdest);
! 		pat = expdest;
! 		/* FALLTHROUGH */
! 		easy = 0;
! 
! 	case VSASSIGN:
! 	case VSQUESTION:
! 		if (easy)
! 			goto record;
! 		/* FALLTHROUGH */
! 
! 		{
! 
  			char *startp;
+ 			char *loc;
+ 			int c = 0;
  			int saveherefd = herefd;
  			struct nodelist *saveargbackq = argbackq;
  			herefd = -1;
***************
*** 501,521 ****
  			herefd = saveherefd;
  			argbackq = saveargbackq;
  			startp = stackblock() + startloc;
! 			if (subtype == VSASSIGN) {
  				setvar(var, startp, 0);
  				STADJUST(startp - expdest, expdest);
! 				varflags &=~ VSNUL;
  				goto again;
  			}
- 			/* subtype == VSQUESTION */
- 			if (*p != CTLENDVAR) {
- 				outfmt(&errout, "%s\n", startp);
- 				error((char *)NULL);
- 			}
- 			error("%.*s: parameter %snot set", p - var - 1,
- 				var, (varflags & VSNUL)? "null or " : nullstr);
  		}
  	}
  	if (subtype != VSNORMAL) {	/* skip to end of alternative */
  		int nesting = 1;
  		for (;;) {
--- 555,639 ----
  			herefd = saveherefd;
  			argbackq = saveargbackq;
  			startp = stackblock() + startloc;
! 
! 			switch (subtype) {
! 			case VSASSIGN:
  				setvar(var, startp, 0);
  				STADJUST(startp - expdest, expdest);
! 				varflags &= ~VSNUL;
! 				if (c != 0)
! 					*loc = c;
  				goto again;
+ 
+ 			case VSQUESTION:
+ 				if (*p != CTLENDVAR) {
+ 					outfmt(&errout, "%s\n", startp);
+ 					error((char *)NULL);
+ 				}
+ 				error("%.*s: parameter %snot set", p - var - 1,
+ 				      var, (varflags & VSNUL) ? "null or "
+ 							      : nullstr);
+ 				break;
+ 
+ 			case VSTRIMLEFT:
+ 				for (loc = startp; loc < pat - 1; loc++) {
+ 					c = *loc;
+ 					*loc = '\0';
+ 					if (patmatch(pat, startp)) {
+ 						*loc = c;
+ 						goto recordleft;
+ 					}
+ 					*loc = c;
+ 				}
+ 				break;
+ 
+ 			case VSTRIMLEFTMAX:
+ 				for (loc = pat - 1; loc >= startp; loc--) {
+ 					c = *loc;
+ 					*loc = '\0';
+ 					if (patmatch(pat, startp)) {
+ 						*loc = c;
+ 						goto recordleft;
+ 					}
+ 					*loc = c;
+ 				}
+ 				break;
+ 
+ 			case VSTRIMRIGHT:
+ 				for (loc = pat - 1; loc >= startp; loc--) {
+ 					if (patmatch(pat, loc)) {
+ 						expdest = loc;
+ 						goto record;
+ 					}
+ 				}
+ 				break;
+ 
+ 			case VSTRIMRIGHTMAX:
+ 				for (loc = startp; loc < pat - 1; loc++) {
+ 					if (patmatch(pat, loc)) {
+ 						expdest = loc;
+ 						goto record;
+ 					}
+ 				}
+ 				break;
+ 
+ recordleft:
+ 				expdest = (pat - 1) - (loc - startp);
+ 				while (loc != pat - 1)
+ 					*startp++ = *loc++;
+ 				goto record;
+ 
+ 			default:
+ 				abort();
  			}
  		}
+ 		break;
+ 
+ 
+ 	default:
+ 		abort();
  	}
+ 
  	if (subtype != VSNORMAL) {	/* skip to end of alternative */
  		int nesting = 1;
  		for (;;) {
***************
*** 577,583 ****
  	int allow_split;
  {
  	int num;
- 	char temp[32];
  	char *p;
  	int i;
  	extern int exitstatus;
--- 695,700 ----
***************
*** 613,625 ****
  	case '!':
  		num = backgndpid;
  numvar:
! 		p = temp + 31;
! 		temp[31] = '\0';
! 		do {
! 			*--p = num % 10 + '0';
! 		} while ((num /= 10) != 0);
! 		while (*p)
! 			STPUTC(*p++, expdest);
  		break;
  	case '-':
  		for (i = 0 ; i < NOPTS ; i++) {
--- 730,736 ----
  	case '!':
  		num = backgndpid;
  numvar:
! 		cvtnum(num, expdest);
  		break;
  	case '-':
  		for (i = 0 ; i < NOPTS ; i++) {
*** 1.1	1995/01/13 00:50:03
--- extern.h	1995/01/13 01:46:00
***************
*** 31,34 ****
  void readcmdfile __P((char *));
  void trargs __P((char **));
  void trputs __P((char *));
! 
--- 31,34 ----
  void readcmdfile __P((char *));
  void trargs __P((char **));
  void trputs __P((char *));
! char *cvtnum __P((int, char *));
*** 1.1	1995/01/13 00:27:01
--- main.c	1995/01/13 02:59:09
***************
*** 213,219 ****
  			flushout(&output);
  		}
  		n = parsecmd(inter);
! 		/* showtree(n); DEBUG */
  		if (n == NEOF) {
  			if (!top || numeof >= 50)
  				break;
--- 213,221 ----
  			flushout(&output);
  		}
  		n = parsecmd(inter);
! #if DEBUG > 1
! 		showtree(n);
! #endif
  		if (n == NEOF) {
  			if (!top || numeof >= 50)
  				break;
*** 1.1	1994/12/19 01:36:32
--- parser.c	1995/01/13 04:15:03
***************
*** 1136,1142 ****
  		subtype = VSNORMAL;
  		if (c == '{') {
  			c = pgetc();
! 			subtype = 0;
  		}
  		if (is_name(c)) {
  			do {
--- 1136,1147 ----
  		subtype = VSNORMAL;
  		if (c == '{') {
  			c = pgetc();
! 			if (c == '#') {
! 				subtype = VSLENGTH;
! 				c = pgetc();
! 			}
! 			else
! 				subtype = 0;
  		}
  		if (is_name(c)) {
  			do {
***************
*** 1152,1165 ****
  		STPUTC('=', out);
  		flags = 0;
  		if (subtype == 0) {
! 			if (c == ':') {
  				flags = VSNUL;
  				c = pgetc();
  			}
- 			p = strchr(types, c);
- 			if (p == NULL)
- 				goto badsub;
- 			subtype = p - types + VSNORMAL;
  		} else {
  			pungetc();
  		}
--- 1157,1187 ----
  		STPUTC('=', out);
  		flags = 0;
  		if (subtype == 0) {
! 			switch (c) {
! 			case ':':
  				flags = VSNUL;
  				c = pgetc();
+ 				/*FALLTHROUGH*/
+ 			default:
+ 				p = strchr(types, c);
+ 				if (p == NULL)
+ 					goto badsub;
+ 				subtype = p - types + VSNORMAL;
+ 				break;
+ 			case '%':
+ 			case '#': 
+ 				{
+ 					int cc = c;
+ 					subtype = c == '#' ? VSTRIMLEFT :
+ 							     VSTRIMRIGHT;
+ 					c = pgetc();
+ 					if (c == cc)
+ 						subtype++;
+ 					else
+ 						pungetc();
+ 					break;
+ 				}
  			}
  		} else {
  			pungetc();
  		}
***************
*** 1407,1410 ****
--- 1429,1458 ----
  	default:
  		return "<internal prompt error>";
  	}
+ }
+ 
+ /*
+  * Our own itoa().
+  */
+ char *
+ cvtnum(num, buf)
+ 	int num;
+ 	char *buf;
+ 	{
+ 	char temp[32];
+ 	int neg = num < 0;
+ 	char *p = temp + 31;
+ 
+ 	temp[31] = '\0';
+ 
+ 	do {
+ 		*--p = num % 10 + '0';
+ 	} while ((num /= 10) != 0);
+ 
+ 	if (neg)
+ 		*--p = '-';
+ 
+ 	while (*p)
+ 		STPUTC(*p++, buf);
+ 	return buf;
  }
*** 1.1	1995/01/13 00:14:29
--- parser.h	1995/01/13 00:14:38
***************
*** 48,63 ****
  #define	CTLENDARI '\207'
  
  /* variable substitution byte (follows CTLVAR) */
! #define VSTYPE 07		/* type of variable substitution */
! #define VSNUL 040		/* colon--treat the empty string as unset */
! #define VSQUOTE 0100		/* inside double quotes--suppress splitting */
  
  /* values of VSTYPE field */
! #define VSNORMAL 1		/* normal variable:  $var or ${var} */
! #define VSMINUS 2		/* ${var-text} */
! #define VSPLUS 3		/* ${var+text} */
! #define VSQUESTION 4		/* ${var?message} */
! #define VSASSIGN 5		/* ${var=text} */
  
  
  /*
--- 48,68 ----
  #define	CTLENDARI '\207'
  
  /* variable substitution byte (follows CTLVAR) */
! #define VSTYPE	0x0f		/* type of variable substitution */
! #define VSNUL	0x10		/* colon--treat the empty string as unset */
! #define VSQUOTE 0x80		/* inside double quotes--suppress splitting */
  
  /* values of VSTYPE field */
! #define VSNORMAL	0x1		/* normal variable:  $var or ${var} */
! #define VSMINUS		0x2		/* ${var-text} */
! #define VSPLUS		0x3		/* ${var+text} */
! #define VSQUESTION	0x4		/* ${var?message} */
! #define VSASSIGN	0x5		/* ${var=text} */
! #define VSTRIMLEFT	0x6		/* ${var#pattern} */
! #define VSTRIMLEFTMAX	0x7		/* ${var##pattern} */
! #define VSTRIMRIGHT	0x8		/* ${var%pattern} */
! #define VSTRIMRIGHTMAX 	0x9		/* ${var%%pattern} */
! #define VSLENGTH	0xa		/* ${#var} */
  
  
  /*
*** 1.1	1995/01/13 00:14:42
--- show.c	1995/01/13 00:33:27
***************
*** 71,76 ****
--- 71,79 ----
  	struct nodelist *lp;
  	char *s;
  
+ 	if (n == NULL)
+ 		return;
+ 
  	indent(ind, pfx, fp);
  	switch(n->type) {
  	case NSEMI:
***************
*** 178,187 ****
--- 181,195 ----
  			putc('$', fp);
  			putc('{', fp);
  			subtype = *++p;
+ 			if (subtype == VSLENGTH)
+ 				putc('#', fp);
+ 
  			while (*p != '=')
  				putc(*p++, fp);
+ 
  			if (subtype & VSNUL)
  				putc(':', fp);
+ 
  			switch (subtype & VSTYPE) {
  			case VSNORMAL:
  				putc('}', fp);
***************
*** 197,202 ****
--- 205,226 ----
  				break;
  			case VSASSIGN:
  				putc('=', fp);
+ 				break;
+ 			case VSTRIMLEFT:
+ 				putc('#', fp);
+ 				break;
+ 			case VSTRIMLEFTMAX:
+ 				putc('#', fp);
+ 				putc('#', fp);
+ 				break;
+ 			case VSTRIMRIGHT:
+ 				putc('%', fp);
+ 				break;
+ 			case VSTRIMRIGHTMAX:
+ 				putc('%', fp);
+ 				putc('%', fp);
+ 				break;
+ 			case VSLENGTH:
  				break;
  			default:
  				printf("<subtype %d>", subtype);