/**************************************************************
 *
 *	CRISP - Custom Reduced Instruction Set Programmers Editor
 *
 *	(C) Paul Fox, 1989
 *
 *    Please See COPYRIGHT notice.
 *
 **************************************************************/
# include	"list.h"

# define	DEBUG_REGEXP
# undef	DEBUG_REGEXP

# define	ERROR(x)	{ ewprintf(x); return -1; }
# define	REGEXP_SIZE	512
# define	REGEXP_INCR	128

# define	BITMAP_SIZE	(256 / 8)
# define	FINISH		1	/* End of compiled r.e.		*/
# define	ZERO_OR_MORE	2	/* ..@				*/
# define	ONE_OR_MORE	3	/* ..+				*/
# define	STAR		4	/* *				*/
# define	QUESTION	5	/* ?				*/
# define	CLASS		6	/* [xyz] or [~xyz]		*/
# define	STRING		7	/* xyz...			*/
# define	END		8	/* End of branch list.		*/
# define	OR		9	/* '|' alternatives.		*/
# define	SETPOS		10	/* \c				*/
# define	BOL		11	/* < or ^ or %			*/
# define	EOL		12	/* > or $			*/
# define	LOOP		13	/* Used as end of block terminator*/
					/* for @ and +.			*/
# define	BEGIN_WORD	14	/* Used for \<			*/
# define	END_WORD	15	/* Used for \>			*/
# define	NEWLINE		16	/* \n				*/
# define	OPEN		20	/* '{'				*/
# define	CLOSE		30	/* '}'				*/
int	bittab[] = { 1, 2, 4, 8, 16, 32, 64, 128 };

struct	regopts regopts = {
	TRUE, 
	TRUE,
# if defined(STANDALONE)
	TRUE,		/* Unix style regexps. */
# else
	FALSE,		/* CRISP style regexps. */
# endif
	};

# define	MAGIC_BRIEF	"%@+*?[|{}\\$>"
# define	MAGIC_UNIX	"+*.[|{}\\$>"
char	*re_opcodes[] = {
		"<0>",
		"FINISH",
		"ZERO_OR_MORE",
		"ONE_OR_MORE",
		"STAR",
		"QUESTION",
		"CLASS",
		"STRING",
		"END",
		"OR",
		"SETPOS",
		"BOL",
		"EOL",
		"LOOP", 
		"BEGIN-WORD",
		"END-WORD",
		"NEWLINE", 
		"17", "18", "19",
		"OPEN-0", "OPEN-1", "OPEN-2", "OPEN-3", "OPEN-4", "OPEN-5",
		"OPEN-6", "OPEN-7", "OPEN-8", "OPEN-9", 
		"CLOSE-0", "CLOSE-1", "CLOSE-2", "CLOSE-3", "CLOSE-4", "CLOSE-5",
		"CLOSE-6", "CLOSE-7", "CLOSE-8", "CLOSE-9"
		};

static	char	*start_of_line;
REGEXP	*regexp;
char	*end_ptr;
typedef struct regstate {
	int	loop_count;
	char	*lptr;
	int	size;
	} rsp;

int	bstack[10];
int	re_level;
int	re_group;	/* Used for {..} in translate patterns. */
char	*re_code = NULL;
int	re_code_size = 0;
int	end_of_code;
int	last_end;
char	*pat_ptr;
int	allow_modifier;
int	or_just_done = FALSE;
extern int dflag;
void	re_print();
static char	*reg_print_bitmap();
void	shift_up();
int	re_comp();
int	gen_atom PROTO((void));
int	regmatch PROTO((char *, char *, int, rsp *));
int	case_match PROTO((char *, char *, int));
static int	word_ch PROTO((int));

# if !defined(STANDALONE)
/**********************************************************************/
/*   Function  called  by  re_search()  primitive to get and set the  */
/*   syntax mode.						      */
/**********************************************************************/
int
set_re_syntax(mode)
int	mode;
{	int	old_mode = regopts.unix_re;

	regopts.unix_re = mode;
	return old_mode;
}
void
re_syntax_fn()
{
	acc_assign_int((long) regopts.unix_re);
	if (argv[1].l_flags == F_INT && (argv[1].l_int == 0 || argv[1].l_int == 1))
		regopts.unix_re = argv[1].l_int;
}
# endif
REGEXP *
regcomp(pattern)
char	*pattern;
{
	static REGEXP regexp;
	register char *re;
	char	*next_block();

	if (re_code == NULL) {
		re_code = (char *) chk_alloc(REGEXP_SIZE);
		re_code_size = REGEXP_SIZE;
		}
	end_of_code = 0;
	re_group = re_level = 0;
	pat_ptr = pattern;
	last_end = -1;
	allow_modifier = FALSE;

	if (re_comp() < 0)
		return NULL;
	if (re_level) {
		ewprintf("Missing close brace");
		return NULL;
		}

	re_code[end_of_code] = FINISH;
	regexp.program = re_code;
	if (dflag & DB_REGEXP) {
		trace_log("Pass 1:\n");
		re_print(re_code);
		}

	/***********************************************/
	/*   Following  piece  of code walks down the  */
	/*   regular   expression  and  sets  up  the  */
	/*   link   fields   needed   by  the  '|'-OR  */
	/*   pattern matching code.		       */
	/***********************************************/
	for (re = re_code; *re != FINISH; ) {
		register char *re1;
		register char *re2;

		if (*re == ZERO_OR_MORE || *re == ONE_OR_MORE) {
			re += 3;
			continue;
			}
		if (*re != OR) {
			re += *re == END ? 3 : LGET16(re);
			continue;
			}
		re1 = re + 3;
		re1 = next_block(re1);
		if (*re1 != END) {
			ewprintf("Internal inconsistency.");
			return NULL;
			}
		/*--------------------------------
		 *   Update link field for OR.
		 *--------------------------------*/
		LPUT16((LIST *) re, re1 - re + 3);
		/*--------------------------------
		 *   Update END field.
		 *--------------------------------*/
		if ((re2 = next_block(re1)) == NULL) {
			ewprintf("Internal inconsistency (1).");
			return NULL;
			}
		while (1) {
			if (*re2 != OR) {
				if ((re2 = next_block(re2)) == NULL) {
					ewprintf("Internal inconsistency (2).");
					return NULL;
					}
				break;
				}
			re2 += 3;
			while (*re2 != END && *re2 != FINISH)
				if ((re2 = next_block(re2)) == NULL) {
					ewprintf("Internal inconsistency (3).");
					return NULL;
					}
			if (*re2 == END)
				re2 += 3;
			}
		LPUT16((LIST *) re1, re2 - re1);
		re += 3;
		}
	if (dflag & DB_REGEXP) {
		trace_log("Pass 2:\n");
		re_print(re_code);
		}
	return &regexp;
}
char *
next_block(re)
register char	*re;
{	register int	i;

	if (*re >= OPEN && *re <= OPEN + 9) {
		int close = *re + 10;
		while (*re != close) {
			i = (*re == OR || *re == END) ? 3 : LGET16(re);
			if (i == 0)
				return NULL;
			re += i;
			}
		}
	if (*re == OR || *re == END)
		return re+3;
	return re + LGET16(re);
}
int
re_comp()
{	int	allow_or = FALSE;
	int	last_end_on_entry = last_end;
# define	MAX_OR		32

	or_just_done = FALSE;
	while (*pat_ptr) {
		if (!regopts.regexp_chars) {
			pat_ptr++;
			goto DEFAULT;
			}
		switch (*pat_ptr++) {
		  case '*':
		  	if (regopts.unix_re == FALSE)
				goto DEFAULT;
			/* Fall thru.. */
		  case '@':
		  	if (pat_ptr[-1] == '@' && regopts.unix_re)
				goto DEFAULT;
			/* Fall thru.. */
		  case '+':
			if (allow_modifier == FALSE)
				goto DEFAULT;
			shift_up(last_end);
			re_code[last_end] = pat_ptr[-1] == '+' ? ONE_OR_MORE 
							    : ZERO_OR_MORE;
			re_code[end_of_code] = LOOP;		/* NEW */
			LPUT16((LIST *) &re_code[end_of_code], 3);
			end_of_code += 3;
			LPUT16((LIST *) &re_code[last_end], end_of_code - last_end);
			allow_modifier = FALSE;
			break;
		  case '|':
			if (allow_or == FALSE)
				ERROR("Null expression before |");
			shift_up(last_end);
			re_code[last_end] = OR;
			allow_or = FALSE;
			re_code[end_of_code] = END;
			LPUT16((LIST *) &re_code[end_of_code], 0);
			end_of_code += 3;
			or_just_done = TRUE;
			continue;
		  case '{':
		  	if (regopts.unix_re)
				goto DEFAULT;
open_bracket:
			if (re_level > NSUBEXP)
				ERROR("Too many '{'");
			last_end = end_of_code;
			re_code[end_of_code] = OPEN + re_group;
			bstack[re_level++] = re_group;
			if (re_group < 9)
				re_group++;
			LPUT16((LIST *) &re_code[end_of_code], 3);
			end_of_code += 3;
			if (re_comp() < 0)
				return -1;
			re_level--;
			allow_or = TRUE;
			break;
		  case '}':
		  	if (regopts.unix_re)
				goto DEFAULT;
close_bracket:
			re_code[end_of_code] = CLOSE + bstack[re_level-1];
			LPUT16((LIST *) &re_code[end_of_code], 3);
			end_of_code += 3;
			allow_modifier = TRUE;
			last_end = last_end_on_entry;
			return 0;
		  case '\\':
		  	if (regopts.unix_re) {
				switch (*pat_ptr) {
				  case '(':
				  	pat_ptr++;
				  	goto open_bracket;
				  case ')':
				  	pat_ptr++;
				  	goto close_bracket;
				  }
				}
			/* Fall thru .. */
		  DEFAULT:
		  default:
			pat_ptr--;
			last_end = end_of_code;
			allow_or = TRUE;
			allow_modifier = TRUE;
			if (gen_atom() == FALSE)
				return -1;
			LPUT16((LIST *) &re_code[last_end], end_of_code - last_end);
			break;
		  }
		or_just_done = FALSE;
		}
	return	0;
}
int
gen_atom()
{	int	len;
	int	size = 3;
	int	incr = 1;
	char *magic_str = regopts.unix_re ? MAGIC_UNIX : MAGIC_BRIEF;
	
	if (!regopts.regexp_chars)
		goto DEFAULT;
	switch (*pat_ptr) {
	  case '<': 
	  case '%':
	  		if (regopts.unix_re)
				goto DEFAULT;
			/* Fallthru.. */
	  case '^': 
			re_code[end_of_code] = BOL;	
			break;
	  case '>': 
	  		if (regopts.unix_re)
				goto DEFAULT;
			/* Fallthru.. */
	  case '$':
			re_code[end_of_code] = EOL;	
			break;
	  case '.':
	  		if (!regopts.unix_re)
				goto DEFAULT;
	  		re_code[end_of_code] = QUESTION;	
			break;
	  case '?':
	  		if (regopts.unix_re)
				goto DEFAULT;
	  		re_code[end_of_code] = QUESTION;	
			break;
	  case '*':	re_code[end_of_code] = STAR;	break;
	  case '[': {
			register int class;
			register int classend;
			char	bitmap[BITMAP_SIZE];
			int	i;
			int	not = 0;
# define	SET(x)	bitmap[x >> 3] |= bittab[x & 7]
# define	ISSET(ptr, bit) (ptr[bit >> 3] & bittab[bit & 7])

			for (i = 0; i < BITMAP_SIZE; i++)
				bitmap[i] = 0;

			re_code[end_of_code] = CLASS;
			pat_ptr++;
		 	if (*pat_ptr == '^' || *pat_ptr == '~') {
				not = 1;
			       	pat_ptr++;
				}
		       while (*pat_ptr == ']' || *pat_ptr == '-') {
				SET(*pat_ptr);
				pat_ptr++;
				}
		       while (*pat_ptr && *pat_ptr != ']') {
			       if (*pat_ptr == '-') {
				       pat_ptr++;
				       if (*pat_ptr == ']' || *pat_ptr == 0) {
						SET('-');
						continue;
						}
				       class = (pat_ptr[-2] & 0xff)+1;
				       classend = *pat_ptr & 0xff;
				       if (class > classend+2) {
						ewprintf("Invalid [] range.");
						return FALSE;
						}
				       for (; class <= classend; class++)
						SET(class);
				       pat_ptr++;
				       }
				else if (*pat_ptr == '\\') {
					int	ch;
					pat_ptr++;
					ch = *pat_ptr == 'n' ? '\n' :
					     *pat_ptr == 'f' ? '\f' : 
					     *pat_ptr == 't' ? '\t' : 
					     *pat_ptr;
					SET(ch);
					pat_ptr++;
					}
				else {
					SET(*pat_ptr);
					pat_ptr++;
					}
		       }
		       if (*pat_ptr++ != ']') {
				ewprintf("Unmatched [ ]");
				return FALSE;
				}
			for (i = 0; i < BITMAP_SIZE; i++)
				re_code[end_of_code + 3 + i] =
					not ? ~bitmap[i] : bitmap[i];
			end_of_code += 3 + i;
			return TRUE;
	       }
	  case '\\':
		if (*++pat_ptr == 'c') {
			re_code[end_of_code] = SETPOS;
			break;
			}
  		if (regopts.unix_re) {
			if (*pat_ptr == '<') {
				re_code[end_of_code] = BEGIN_WORD;
				break;
				}
			if (*pat_ptr == '>') {
				re_code[end_of_code] = END_WORD;
				break;
				}
			}
		len = strcspn(pat_ptr + 1, magic_str) + 1;
		goto do_normal;
	  DEFAULT:
	  default:
		if (regopts.regexp_chars)
			len = strcspn(pat_ptr, magic_str);
		else
			len = strlen(pat_ptr);
do_normal:
		if (or_just_done && len > 1)
			len = 1;
		else if (len <= 0)
			len = strlen(pat_ptr);
		else if (len > 1 && regopts.regexp_chars) {
			/***********************************************/
			/*   If  string  is  followed  by a repeat or  */
			/*   alternation       we       need       to  */
			/*   include/not-include  the  last character  */
			/*   because  we  need  to obey the differing  */
			/*   precedence  between  CRISP  regexps  and  */
			/*   Unix regexps.			       */
			/***********************************************/
			if (pat_ptr[len] == '@' || 
			    pat_ptr[len] == '*' || 
			    pat_ptr[len] == '+' || 
			    pat_ptr[len] == '|')
				len--;
			}

		/***********************************************/
		/*   Check to see if we need to expand code[]  */
		/***********************************************/
		if (end_of_code + len >= re_code_size - REGEXP_INCR) {
			/***********************************************/
			/*   We  lose  memory  here.  Won't  fix this  */
			/*   just yet but needs looking into.	       */
			/***********************************************/
			char *new_code = (char *) chk_alloc(re_code_size + REGEXP_INCR);
			memcpy(new_code, re_code, re_code_size);
			re_code_size += REGEXP_INCR;
			re_code = new_code;
			}
		re_code[end_of_code] = STRING;
		re_code[end_of_code+3] = len;
		strncpy(&re_code[end_of_code+4], pat_ptr, len);
		incr = len;
		size = len + 4;
		break;
	  }
	pat_ptr += incr;
	if (*pat_ptr == '}' || *pat_ptr == NULL)
		LPUT16((LIST *) &re_code[end_of_code], 0);
	else
		LPUT16((LIST *) &re_code[end_of_code], size);
	end_of_code += size;
	return TRUE;
}
void
shift_up(index)
int	index;
{
	register	char	*dst = &re_code[end_of_code - 1] + 3;
	register	char	*src = dst - 3;
	register	char	*ptr = &re_code[index];

	while (src >= ptr)
		*dst-- = *src--;
	end_of_code += 3;
}

int
regexec(prog, string, size)
REGEXP *prog;
char *string;
int	size;
{	int	incr = 1;
	register int	loop_count;
	register char	*lptr;
	char	*prog_string = prog->program;

	if (*prog_string == BOL) {
		if (regopts.fwd_search && regopts.offset)
			return FALSE;
		if (!regopts.fwd_search)
			regopts.offset = 0;
		}
	lptr = string + regopts.offset;
	start_of_line = string;
	size -= regopts.offset;
	regexp = prog;
	prog->setpos = NULL;

	if (regopts.fwd_search) {
		loop_count = size + 1;
		if (*prog_string == STRING) {
			char ch4 = prog_string[4];
			loop_count -= prog_string[3] - 1;
			if (regopts.case_sense && size) {
				int orig_loop_count = loop_count;
				while (--loop_count > 0) {
					if (*lptr++ == ch4) {
						if (strncmp(lptr-1, &prog_string[4], prog_string[3]) == 0) {
							if (*(prog_string + LGET16(prog_string)) == FINISH) {
								prog->start = lptr-1;
								prog->end = lptr + prog_string[3] - 1;
								return 1;
								}
							lptr--;
							break;
							}
						}
					}
				loop_count++;
				size -= orig_loop_count - loop_count;
				}
				if (loop_count <= 0)
					return 0;
			}
		}
	else {
		loop_count = lptr - string + 1;
		incr = -1;
		}
	if (*prog_string == BOL)
		loop_count = 1;
		
	while (loop_count-- > 0) {
		if ((prog->length = regmatch(prog_string, lptr, size, (rsp *) NULL)) >= 0) {
			prog->start = lptr;
			prog->end = lptr + prog->length;
			return 1;
			}
		lptr += incr;
		size -= incr;
		}
	return 0;
}
/**********************************************************************/
/*   Recursive  routine  to  perform  pattern  matching. Returns the  */
/*   length of the matched text or -1 if no match was found.	      */
/**********************************************************************/
int
regmatch(prog, lptr, size, regstate)
register char	*prog;
register char	*lptr;
int	size;
rsp *regstate;
{	int	i, ret;
	char	*prog_start = prog;
	char	*start_lptr = lptr;

loop_again:
	for ( ; ; prog += LGET16(prog)) {
		switch (*prog) {
		  case STRING:
			i = prog[3];
			if (size < i)
				return -1;
			if (prog[4] != *lptr ||
				strncmp(lptr, prog+4, i) != 0) {
				if (!regopts.case_sense) {
					if (case_match(lptr, prog+4, i) == FALSE)
						return -1;
					}
				else
					return -1;
				}
			size -= i;
			lptr += i;
			break;
		  case CLASS: {
		  	char *bitmap = prog + 3;
			char	ch = '\n';
/*			if (size > 0)
				ch = *lptr;*/
			if (size <= 0)
				return -1;
			ch = *lptr;
			if (ISSET(bitmap, ch)) {
				size--;
				lptr++;
				break;
				}
			if (!regopts.case_sense && isalpha(ch)) {
				char	ch1 = isupper(ch) ? ch + 0x20 : ch;
				char	ch2 = ch1 - 0x20;
				if (!ISSET(bitmap, ch1) && !ISSET(bitmap, ch2))
					return -1;
				}
			else 
				return -1;
			size--;
			lptr++;
			break;
			}
		  case FINISH:
		  	return lptr - start_lptr;
		  case LOOP:		
			if (regstate && --regstate->loop_count > 0) {
				prog = prog_start;
				goto loop_again;
				}
			break;
		  case END:	
			break;
		  case BOL:
			if (lptr != start_of_line)
				return -1;
			break;
		  case EOL:
			if (size) {
				if (*lptr != '\n')
					return -1;
				lptr++, size--;
				}
			break;
		  case QUESTION:
			if (size <= 0)
				return -1;
			size--;
			lptr++;
			break;
		  case OR:
			do {
				if ((ret = regmatch(prog + 3, lptr, size, (rsp *) NULL)) >= 0)
					return (lptr - start_lptr) + ret;
				prog += LGET16(prog);
				}
			while (*prog == OR);
			if ((ret = regmatch(prog, lptr, size, (rsp *) NULL)) >= 0)
				return (lptr - start_lptr) + ret;
			return -1;
		  case STAR: {
		  	char *eptr;
			if (regopts.regexp_chars > 0) {
				for (i = size; i >= 0; i--)
					if ((ret = regmatch(prog + 3, lptr + i, size - i, (rsp *) NULL)) >= 0)
						return (lptr - start_lptr) + i + ret;
				}
			else {
				for (i = size, eptr = lptr; i >= 0; eptr++, i--)
					if ((ret = regmatch(prog + 3, eptr, i, (rsp *) NULL)) >= 0)
						return (lptr - start_lptr) + i + ret;
				}
			return -1;
			}
		  case ONE_OR_MORE:
		  case ZERO_OR_MORE: {
			char *eptr = prog + LGET16(prog);
			char	*next_prog = prog + 3;
			int	matched_len = -1;
			rsp rs;
			REGEXP	saved_regexp;
			saved_regexp = *regexp;
			
			end_ptr = lptr;
			i = *prog == ZERO_OR_MORE ? 1 : 2;
			for ( ; i < size+2; i++) {
				rs.loop_count = i-1;
				if ((ret = regmatch(i == 1 ? eptr : next_prog, 
					lptr, size, &rs)) < 0) {
					if (rs.loop_count)
						break;
					if (i >= size + 3)
						break;
					}
				else {
					matched_len = ret;
					saved_regexp = *regexp;
					if (matched_len >= size)
						break;
					if (regopts.regexp_chars > 0)
						break;
					}
				}
			*regexp = saved_regexp;
			if (*prog == ZERO_OR_MORE && i == 1 && 
			    matched_len < 0)
				break;
			if (matched_len < 0)
				return -1;
# ifdef DEBUG_REGEXP
	print_match(lptr, latest_end_ptr);
# endif
			return (lptr - start_lptr) + matched_len;
			}
		  case OPEN: case OPEN+1: case OPEN+2: case OPEN+3: case OPEN+4:
		  case OPEN+5: case OPEN+6: case OPEN+7: case OPEN+8: case OPEN+9:
		  	i = *prog - OPEN;
			regexp->startp[i] = lptr;
			break;
		  case CLOSE: case CLOSE+1: case CLOSE+2: case CLOSE+3: case CLOSE+4:
		  case CLOSE+5: case CLOSE+6: case CLOSE+7: case CLOSE+8: case CLOSE+9:
		  	i = *prog - CLOSE;
			regexp->endp[i] = lptr;
			if (i > regexp->exp_level)
				regexp->exp_level = i;
			break;
		  case SETPOS:
			regexp->setpos = lptr;
			break;
		  case BEGIN_WORD:
		  	if (lptr == start_of_line)
				break;
			if (word_ch(*lptr) && !word_ch(lptr[-1]))
			    	break;
			return -1;
		  case END_WORD:
		  	if (size == 0)
				break;
			if (word_ch(*lptr))
				return -1;
		  	break;
		  }
		}
}
/**********************************************************************/
/*   Function to determine if character is a 'word' type character.   */
/**********************************************************************/
static int
word_ch(ch)
int	ch;
{
	return isdigit(ch) || isalpha(ch) || ch == '_';
}
int
case_match(str1, str2, len)
register char *str1;
register char *str2;
register int len;
{	register int ch1, ch2;
	while (len-- > 0) {
		ch1 = *str1++;
		ch2 = *str2++;
		if (isupper(ch1))
			ch1 += 0x20;
		if (isupper(ch2))
			ch2 += 0x20;
		if (ch1 != ch2)
			return FALSE;
		}
	return TRUE;
}
void
re_print(re)
char	*re;
{	char	*start = re;
	char	buf[128];
	char	buf1[128];
	char	buf2[20];
	char	*p;
	int	i;

	trace_log("\n");
	while (1) {
		p = buf1;
		switch (*re) {
		  case FINISH:	
		  case ZERO_OR_MORE:
		  case ONE_OR_MORE:
		  case STAR:	
		  case QUESTION:
		  case BOL:	
		  case EOL:	
		  case END:
		  case OR:
		  case LOOP:
		  case SETPOS:
				p = re_opcodes[*re];
				break;
		  case CLASS:		
					sprintf(buf1, "CLASS     %s",
						reg_print_bitmap(re+3));
					break;
		  case STRING:		
				sprintf(buf1, "STRING  len=%d str='%.*s'",
					re[3], re[3], re+4);
				break;
		  case OPEN:   case OPEN+1: case OPEN+2: case OPEN+3: 
		  case OPEN+4: case OPEN+5: case OPEN+6: case OPEN+7: 
		  case OPEN+8: case OPEN+9:
				sprintf(buf2, "OPEN-%d", *re - OPEN);
				p = buf2;
				break;
		  case CLOSE:   case CLOSE+1: case CLOSE+2: case CLOSE+3: 
		  case CLOSE+4: case CLOSE+5: case CLOSE+6: case CLOSE+7: 
		  case CLOSE+8: case CLOSE+9:
				sprintf(buf2, "CLOSE-%d", *re - CLOSE);
				p = buf2;
				break;
		  default:
				sprintf(buf1, "** DONT KNOW = 0x%02x", *re);
				break;
		  }
		if (*re == FINISH)
			sprintf(buf, "[%02d] --> --/-- %s\n", 
			re-start, p);
		else
			sprintf(buf, "[%02d] --> %02d/%02d %s\n", 
				re-start, LGET16(re), 
				(re - start) + LGET16(re), p);
		trace_log(buf);
		if (*re == FINISH)
			break;
		if (*re == OR || *re == END || *re == ZERO_OR_MORE || *re == ONE_OR_MORE)
			i = 3;
		else
			i = LGET16(re);
		if (i == 0)
			i = 3;
		re += i;
		}
}
static char	*
reg_print_bitmap(bitmap)
char	*bitmap;
{
	int	i;
	int	not = 0;
	int	last_set = -1;
	static	char	buf[128];
	char	*cp = buf;

	*cp++ = '[';
	if (ISSET(bitmap, 0)) {
		*cp++ = '~';
		not = 1;
		}
	for (i = 0; i < 256; i++) {
		int	set = ISSET(bitmap, i);
		if (not)
			set = !set;
		if (set) {
			if (last_set == -1) {
				if (i == '\t')
					*cp++ = '\\', *cp++ = 't';
				else if (i == '\n')
					*cp++ = '\\', *cp++ = 'n';
				else
					*cp++ = (char) i;
				last_set = i;
				}
			continue;
			}
		if (last_set != -1) {
			if (i > last_set+1) {
				*cp++ = '-';
				*cp++ = (char) i;
				}
			last_set = -1;
			}
		}
	*cp++ = ']';
	*cp = NULL;
	return buf;
}
# if defined(DEBUG_REGEXP)
print_match(start, end)
char	*start;
char	*end;
{
	extern int dflag;
	if (dflag & DB_REGEXP) {
		int	i, j;
		char	*cp;
		trace_log("Matched: '");
		for (cp = start_of_line; *cp; cp++)
			if (*cp == '\n')
				trace_log("\\n");
			else
				trace_log("%c", *cp);
		trace_log("'\n          ");
		for (i = start - start_of_line; i > 0; i--)
			trace_log(" ");
		trace_log("^");
		if (j = end - start) {
			for (i = j - 2; i > 0; i--)
				trace_log("-");
			trace_log("^");
			}
		trace_log("\n");
		}
}
# endif
