Module Name:    othersrc
Committed By:   agc
Date:           Wed Jun 21 23:36:17 UTC 2023

Modified Files:
        othersrc/external/bsd/elex: TODO
        othersrc/external/bsd/elex/dist: Makefile agcre.c agcre.h elex.c elex.h
            main.c
        othersrc/external/bsd/elex/dist/tests: 28.expected
        othersrc/external/bsd/elex/lib: Makefile
Added Files:
        othersrc/external/bsd/elex/dist: gap.c gap.h
Removed Files:
        othersrc/external/bsd/elex/dist: striter.c striter.h

Log Message:
Elex version 20230621
=====================

+ agcre - added internal magic numbers to agcre to attempt to catch
  misbehaving programs overwriting sections of memory (extremely coarse-
  grained checks here).
+ agcre - check internal magic numbers before attempting to execute
  regex programs
+ agcre - bump agcre magic number in the external structure
+ elex - remove striter (simple) and move to using buffer gap functions
+ elex - fix a bug whereby we check if a rule has a return value
  before attempting to parse that return value.
+ elex - fix up tests for all of these fixes
+ elex - error out when reading rules if a bad rule is encountered (as
  the resulting lexer would be erroneous if we continued)
+ elex - bump elex version to 20230621


To generate a diff of this commit:
cvs rdiff -u -r1.1 -r1.2 othersrc/external/bsd/elex/TODO
cvs rdiff -u -r1.2 -r1.3 othersrc/external/bsd/elex/dist/Makefile \
    othersrc/external/bsd/elex/dist/agcre.h
cvs rdiff -u -r1.3 -r1.4 othersrc/external/bsd/elex/dist/agcre.c \
    othersrc/external/bsd/elex/dist/elex.c \
    othersrc/external/bsd/elex/dist/elex.h \
    othersrc/external/bsd/elex/dist/main.c
cvs rdiff -u -r0 -r1.1 othersrc/external/bsd/elex/dist/gap.c \
    othersrc/external/bsd/elex/dist/gap.h
cvs rdiff -u -r1.3 -r0 othersrc/external/bsd/elex/dist/striter.c
cvs rdiff -u -r1.2 -r0 othersrc/external/bsd/elex/dist/striter.h
cvs rdiff -u -r1.3 -r1.4 othersrc/external/bsd/elex/dist/tests/28.expected
cvs rdiff -u -r1.2 -r1.3 othersrc/external/bsd/elex/lib/Makefile

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: othersrc/external/bsd/elex/TODO
diff -u othersrc/external/bsd/elex/TODO:1.1 othersrc/external/bsd/elex/TODO:1.2
--- othersrc/external/bsd/elex/TODO:1.1	Thu Dec  9 04:15:25 2021
+++ othersrc/external/bsd/elex/TODO	Wed Jun 21 23:36:17 2023
@@ -44,3 +44,5 @@ get line number action
 bookmarks
 clone
 read-defs from read-grammar
+bug fix - test we have an m[6] match before using it as return value
+move from striter to gap functions

Index: othersrc/external/bsd/elex/dist/Makefile
diff -u othersrc/external/bsd/elex/dist/Makefile:1.2 othersrc/external/bsd/elex/dist/Makefile:1.3
--- othersrc/external/bsd/elex/dist/Makefile:1.2	Tue Apr 25 20:03:39 2023
+++ othersrc/external/bsd/elex/dist/Makefile	Wed Jun 21 23:36:17 2023
@@ -1,7 +1,7 @@
 PROG=	elex
 SRCS+=	agcre.c
 SRCS+=	elex.c
-SRCS+=	striter.c
+SRCS+=	gap.c
 SRCS+=	main.c
 MKMAN=	no
 WARNS=	5
Index: othersrc/external/bsd/elex/dist/agcre.h
diff -u othersrc/external/bsd/elex/dist/agcre.h:1.2 othersrc/external/bsd/elex/dist/agcre.h:1.3
--- othersrc/external/bsd/elex/dist/agcre.h:1.2	Wed Feb 22 01:01:39 2023
+++ othersrc/external/bsd/elex/dist/agcre.h	Wed Jun 21 23:36:17 2023
@@ -53,7 +53,7 @@
 #define REG_SUCCESS		AGCRE_REG_SUCCESS
 #define REG_FAILURE		AGCRE_REG_FAILURE
 #define REG_NOMATCH		AGCRE_REG_FAILURE
-#define REG_MAGIC		AGCRE_MAGIC
+#define REG_MAGIC		AGCRE_MAGIC2
 #define REG_MAX_SUBEXPR		AGCRE_MAX_SUBEXPR
 #define REG_MAX_EXPR_LENGTH	AGCRE_MAX_EXPR_LENGTH
 #define REG_ANCHOR		AGCRE_REG_ANCHOR
@@ -89,7 +89,7 @@
 #define AGCRE_REG_SUCCESS	0
 #define AGCRE_REG_FAILURE	1
 
-#define AGCRE_MAGIC		0x20170801
+#define AGCRE_MAGIC2		0x20230621
 
 /* limits we impose on expressions */
 #define AGCRE_MAX_SUBEXPR	100

Index: othersrc/external/bsd/elex/dist/agcre.c
diff -u othersrc/external/bsd/elex/dist/agcre.c:1.3 othersrc/external/bsd/elex/dist/agcre.c:1.4
--- othersrc/external/bsd/elex/dist/agcre.c:1.3	Thu Feb 23 19:36:07 2023
+++ othersrc/external/bsd/elex/dist/agcre.c	Wed Jun 21 23:36:17 2023
@@ -159,17 +159,26 @@ typedef struct threadlist_t {
 	re_thread_t	t[1];			/* the threads */
 } threadlist_t;
 
+#define MAGIC1		0xac1deaf0
+#define MAGIC2		0x41525345
+#define MAGIC3		0xd0d0d00d
+#define MAGIC4		0x666f7572
+
 /* regular expression internals */
 typedef struct re_t {
+	uint32_t	 magic1;	/* magic number #1 */
 	instr_t		*prog;		/* start of instructions */
 	uint32_t	 instrc;	/* # of instructions */
 	uint32_t	 gen;		/* generation number */
 	uint32_t	 setc;		/* # of sets */
 	uint32_t	 maxset;	/* allocated # of sets */
+	uint32_t	 magic2;	/* magic number #2 */
 	set_t		*sets;		/* sets */
 	uint32_t	 flags;		/* comp/exec flags */
 	context_t	*ctxlist;	/* list of contexts */
+	uint32_t	 magic3;	/* magic number #3 */
 	instr_t		*pc;		/* prog counter */
+	uint32_t	 magic4;	/* magic number #4 */
 	int		 msgc;		/* # of chars in msg buffer */
 	char		 msg[256];	/* message buffer */
 } re_t;
@@ -2669,6 +2678,22 @@ growspace(char **buf, size_t *size, size
 	return 1;
 }
 
+/* check it was compiled properly */
+static inline int
+good_struct(const agcre_regex_t *agcre)
+{
+	re_t	*re;
+
+	if (agcre == NULL || agcre->re_magic != AGCRE_MAGIC2) {
+		return 0;
+	}
+	if ((re = agcre->re_g) == NULL) {
+		return 0;
+	}
+	return re->magic1 = MAGIC1 && re->magic2 == MAGIC2 &&
+		re->magic3 == MAGIC3 && re->magic4 == MAGIC4;
+}
+
 /***********************************************************/
 
 /* allocate a new structure and return it */
@@ -2697,7 +2722,13 @@ agcre_regcomp(agcre_regex_t *agcre, cons
 		(agcre_regoff_t)(agcre->re_endp - in.s) :
 		(agcre_regoff_t)strlen(in.s);
 	memset(agcre, 0x0, sizeof(*agcre));
-	agcre->re_g = re = in.re = calloc(1, sizeof(*re));
+	if ((agcre->re_g = re = in.re = calloc(1, sizeof(*re))) == NULL) {
+		return AGCRE_REG_FAILURE;
+	}
+	re->magic1 = MAGIC1;
+	re->magic2 = MAGIC2;
+	re->magic3 = MAGIC3;
+	re->magic4 = MAGIC4;
 	if (in.eo - in.so > AGCRE_MAX_EXPR_LENGTH) {
 		re->msgc = snprintf(re->msg, sizeof(re->msg),
 			"expression length %llu larger than %u",
@@ -2739,7 +2770,7 @@ agcre_regcomp(agcre_regex_t *agcre, cons
 	re->pc->op = OP_MATCH;
 	re->pc += 1;
 	re->instrc = re->pc - re->prog;
-	agcre->re_magic = AGCRE_MAGIC;
+	agcre->re_magic = AGCRE_MAGIC2;
 	if (flags & AGCRE_REG_DUMP) {
 		printprog(re);
 	}
@@ -2757,7 +2788,7 @@ agcre_regerror(int errcode, const agcre_
 	re_t	*re;
 
 	USE_ARG(errcode);
-	if (agcre == NULL || size == 0 || errbuf == NULL) {
+	if (!good_struct(agcre) || size == 0 || errbuf == NULL) {
 		return 0;
 	}
 	re = agcre->re_g;
@@ -2782,15 +2813,15 @@ agcre_regexec(agcre_regex_t *agcre, cons
 	re_t		*re;
 	int		 ret;
 	
-	if (agcre == NULL || vs == NULL || (matchc > 0 && m == NULL)) {
+	if (!good_struct(agcre) || vs == NULL || (matchc > 0 && m == NULL)) {
 		return AGCRE_REG_FAILURE;
 	}
 	if ((re = agcre->re_g) == NULL) {
 		return AGCRE_REG_FAILURE;
 	}
-	if (agcre->re_magic != AGCRE_MAGIC) {
+	if (agcre->re_magic != AGCRE_MAGIC2) {
 		re->msgc = snprintf(re->msg, sizeof(re->msg),
-			"bad magic number 0x%x, not 0x%x", agcre->re_magic, AGCRE_MAGIC);
+			"bad magic number 0x%x, not 0x%x", agcre->re_magic, AGCRE_MAGIC2);
 		return AGCRE_REG_FAILURE;
 	}
 	if (matchc > AGCRE_MAX_SUBEXPR) {
@@ -2942,6 +2973,9 @@ agcre_rev_regexec(agcre_regex_t *agcre, 
 	int		found;
 	int		lastmatch;
 
+	if (!good_struct(agcre) || vs == NULL || (matchc > 0 && m == NULL)) {
+		return AGCRE_REG_FAILURE;
+	}
 	if (flags & AGCRE_REG_STARTEND) {
 		from = m[0].rm_so;
 		to = m[0].rm_eo;
@@ -2984,7 +3018,7 @@ agcre_regfree(agcre_regex_t *agcre)
 	uint32_t	 i;
 	re_t		*re;
 
-	if (agcre) {
+	if (agcre && good_struct(agcre)) {
 		if ((re = agcre->re_g) != NULL) {
 			free(re->prog);
 			for (i = 0 ; i < re->setc ; i++) {
Index: othersrc/external/bsd/elex/dist/elex.c
diff -u othersrc/external/bsd/elex/dist/elex.c:1.3 othersrc/external/bsd/elex/dist/elex.c:1.4
--- othersrc/external/bsd/elex/dist/elex.c:1.3	Thu Feb 23 19:36:07 2023
+++ othersrc/external/bsd/elex/dist/elex.c	Wed Jun 21 23:36:17 2023
@@ -34,7 +34,7 @@
 #include <unistd.h>
 
 #include "agcre.h"
-#include "striter.h"
+#include "gap.h"
 #include "elex.h"
 
 /* a rule for matching */
@@ -279,9 +279,9 @@ make_new_type(elex_t *elex, const char *
 }
 
 #define RULE_START_PAT	"<([^>]+)>[ \t]*(.*)</>[ \t]*"
-                        /*1             2 */
+                        /*1     1       2  2 */
 #define RULE_ACTION	"\\{[ \t]*(BEGIN[(]([^)]+)[)];)?[ \t]*(return[ \t]*([^;]+);[ \t]*)?\\}"
-			/*        3        4                  5            6  */
+			/*        3        4     4    3       5            6     6       5 */
 #define STATE_REGEX	"^%(x|state)[ \t]+([a-zA-Z0-9_]+)"
 #define TYPE_REGEX	"^%type[ \t]+([^ \t]+)[ \t]+([a-zA-Z0-9_]+)"
 #define COMMENT_REGEX	"^#[^\n]*"
@@ -291,30 +291,39 @@ static int
 read_defs(elex_t *elex, const char *s, uint64_t len)
 {
 	agcre_regmatch_t	 m[10];
-	agcre_regex_t		 rule;
-	agcre_regex_t		 state;
-	agcre_regex_t		 type;
-	agcre_regex_t		 comment;
-	striter_t		*str;
-	size_t			 size;
+	agcre_regex_t		 comment_regex;
+	agcre_regex_t		 rule_regex;
+	agcre_regex_t		 state_regex;
+	agcre_regex_t		 type_regex;
+	uint64_t		 linec;
+	uint64_t		 size;
+	uint64_t		 i;
+	gap_t			*gap;
 	char			*buf;
 	char			*startstate;
 	char			*newstate;
 	char			*pat;
-	int			 iret;
-
-	if ((str = striter_new()) == NULL) {
-		return 0;
-	}
-	if (!striter_exec(str, "addtext", s, len)) {
-		return 0;
-	}
-	agcre_regcomp(&rule, RULE_START_PAT RULE_ACTION, AGCRE_REG_EXTENDED);
-	agcre_regcomp(&state, STATE_REGEX, AGCRE_REG_EXTENDED);
-	agcre_regcomp(&type, TYPE_REGEX, AGCRE_REG_EXTENDED);
-	agcre_regcomp(&comment, COMMENT_REGEX, AGCRE_REG_EXTENDED);
-	while ((buf = striter_exec_mem(str, "getline", &size)) != NULL) {
-		if (agcre_regexec(&rule, buf, __arraycount(m), m, 0) == 0) {
+	int			 ruleret;
+	int			 type;
+	int			 ok;
+
+	if ((gap = gap_new()) == NULL) {
+		return 0;
+	}
+	if (!gap_exec(gap, "insert", s, len) ||
+	    !gap_exec(gap, "move-to-offset", NULL, 0)) {
+		return 0;
+	}
+	agcre_regcomp(&rule_regex, RULE_START_PAT RULE_ACTION, AGCRE_REG_EXTENDED);
+	agcre_regcomp(&state_regex, STATE_REGEX, AGCRE_REG_EXTENDED);
+	agcre_regcomp(&type_regex, TYPE_REGEX, AGCRE_REG_EXTENDED);
+	agcre_regcomp(&comment_regex, COMMENT_REGEX, AGCRE_REG_EXTENDED);
+	linec = gap_exec(gap, "linec", NULL, 0);
+	for (ok = 0, i = 0 ; i < linec ; i++) {
+		buf = gap_exec_mem(gap, "getline", i, &size);
+		m[0].rm_so = 0;
+		m[0].rm_eo = size;
+		if (agcre_regexec(&rule_regex, buf, __arraycount(m), m, AGCRE_REG_STARTEND) == 0) {
 			asprintf(&startstate, "%.*s",
 				(int)(m[1].rm_eo - m[1].rm_so), &buf[m[1].rm_so]);
 			asprintf(&pat, "%.*s",
@@ -325,41 +334,49 @@ read_defs(elex_t *elex, const char *s, u
 			} else {
 				newstate = strdup(startstate);
 			}
-			if ((iret = findtype(elex, &buf[m[6].rm_so], m[6].rm_eo - m[6].rm_so)) >= 0) {
-				iret = elex->types[iret].val;
+			if (m[6].rm_so < 0) {
+				ruleret = 0;
+			} else if ((type = findtype(elex, &buf[m[6].rm_so], m[6].rm_eo - m[6].rm_so)) >= 0) {
+				ruleret = elex->types[type].val;
 			} else {
-				iret = strtol(&buf[m[6].rm_so], NULL, 0);
+				ruleret = strtol(&buf[m[6].rm_so], NULL, 0);
 			}
-			if (!elex_make_new_rule(elex, startstate, pat, newstate, iret)) {
+			if (!elex_make_new_rule(elex, startstate, pat, newstate, ruleret)) {
 				warnx("can't make rule '%s'", buf);
+				goto bad_rule;
 			}
 			free(startstate);
 			free(newstate);
 			free(pat);
-		} else if (agcre_regexec(&state, buf, __arraycount(m), m, 0) == 0) {
+		} else if (agcre_regexec(&state_regex, buf, __arraycount(m), m, 0) == 0) {
 			asprintf(&newstate, "%.*s",
 				(int)(m[2].rm_eo - m[2].rm_so), &buf[m[2].rm_so]);
 			if (!make_new_state(elex, newstate)) {
 				warnx("can't make state '%s'", buf);
+				goto bad_rule;
 			}
 			free(newstate);
-		} else if (agcre_regexec(&type, buf, __arraycount(m), m, 0) == 0) {
-			if ((iret = findtype(elex, &buf[m[2].rm_so], m[2].rm_eo - m[2].rm_so)) >= 0) {
-				iret = elex->types[iret].val;
+		} else if (agcre_regexec(&type_regex, buf, __arraycount(m), m, 0) == 0) {
+			if ((type = findtype(elex, &buf[m[2].rm_so], m[2].rm_eo - m[2].rm_so)) >= 0) {
+				type = elex->types[type].val;
 			} else {
-				iret = strtoul(&buf[m[2].rm_so], NULL, 0);
+				type = strtoul(&buf[m[2].rm_so], NULL, 0);
 			}
-			make_new_type(elex, &buf[m[1].rm_so], m[1].rm_eo - m[1].rm_so, iret);
-		} else if (agcre_regexec(&comment, buf, __arraycount(m), m, 0) != 0 && buf[0] != '\n') {
+			make_new_type(elex, &buf[m[1].rm_so], m[1].rm_eo - m[1].rm_so, type);
+		} else if (agcre_regexec(&comment_regex, buf, __arraycount(m), m, 0) != 0 && buf[0] != '\n') {
 			warnx("UNRECOGNISED '%s'", buf);
+			goto bad_rule;
 		}
+		free(buf);
 	}
-	agcre_regfree(&comment);
-	agcre_regfree(&type);
-	agcre_regfree(&rule);
-	agcre_regfree(&state);
-	striter_dispose(&str);
-	return 1;
+	ok = 1;
+bad_rule:
+	agcre_regfree(&comment_regex);
+	agcre_regfree(&type_regex);
+	agcre_regfree(&rule_regex);
+	agcre_regfree(&state_regex);
+	gap_dispose(&gap);
+	return ok;
 }
 
 /* read and parse the defs file */
@@ -631,6 +648,8 @@ elex_exec(elex_t *elex, const char *info
 		return (cc < 0 || cc >= (int64_t)elex->markc) ? 0 : elex->marks[cc];
 	case /* "get-rule-count" */ 0x5a55649d:
 		return elex->rulec;
+	case /* "get-version" */ 0xf2712927:
+		return ELEX_H_;
 	case /* "get-yyleng" */ 0x8801d0e2:
 		return elex->yyleng;
 	case /* "get-yyline" */ 0x8801e26c:
Index: othersrc/external/bsd/elex/dist/elex.h
diff -u othersrc/external/bsd/elex/dist/elex.h:1.3 othersrc/external/bsd/elex/dist/elex.h:1.4
--- othersrc/external/bsd/elex/dist/elex.h:1.3	Thu Feb 23 19:36:07 2023
+++ othersrc/external/bsd/elex/dist/elex.h	Wed Jun 21 23:36:17 2023
@@ -23,7 +23,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #ifndef ELEX_H_
-#define ELEX_H_	20230223
+#define ELEX_H_	20230621
 
 #include <inttypes.h>
 
Index: othersrc/external/bsd/elex/dist/main.c
diff -u othersrc/external/bsd/elex/dist/main.c:1.3 othersrc/external/bsd/elex/dist/main.c:1.4
--- othersrc/external/bsd/elex/dist/main.c:1.3	Thu Feb 23 19:36:07 2023
+++ othersrc/external/bsd/elex/dist/main.c	Wed Jun 21 23:36:17 2023
@@ -108,7 +108,7 @@ main(int argc, char **argv)
 	while ((i = getopt(argc, argv, "Vf:gi:")) != -1) {
 		switch(i) {
 		case 'V':
-			printf("elex version " ELEX_VERSION_S(ELEX_VERSION_NUM) "\n");
+			printf("main elex version " ELEX_VERSION_S(ELEX_VERSION_NUM) "\n");
 			exit(EXIT_SUCCESS);
 		case 'f':
 			lexfile = optarg;

Index: othersrc/external/bsd/elex/dist/tests/28.expected
diff -u othersrc/external/bsd/elex/dist/tests/28.expected:1.3 othersrc/external/bsd/elex/dist/tests/28.expected:1.4
--- othersrc/external/bsd/elex/dist/tests/28.expected:1.3	Thu Feb 23 19:36:08 2023
+++ othersrc/external/bsd/elex/dist/tests/28.expected	Wed Jun 21 23:36:17 2023
@@ -1 +1 @@
-elex version 20230223
+main elex version 20230621

Index: othersrc/external/bsd/elex/lib/Makefile
diff -u othersrc/external/bsd/elex/lib/Makefile:1.2 othersrc/external/bsd/elex/lib/Makefile:1.3
--- othersrc/external/bsd/elex/lib/Makefile:1.2	Fri Feb 24 23:07:54 2023
+++ othersrc/external/bsd/elex/lib/Makefile	Wed Jun 21 23:36:17 2023
@@ -1,7 +1,7 @@
 LIB=	elex
 SRCS+=	agcre.c
 SRCS+=	elex.c
-SRCS+=	striter.c
+SRCS+=	gap.c
 CPPFLAGS+=	-I${DIST}
 MKMAN=	no
 WARNS=	5

Added files:

Index: othersrc/external/bsd/elex/dist/gap.c
diff -u /dev/null othersrc/external/bsd/elex/dist/gap.c:1.1
--- /dev/null	Wed Jun 21 23:36:17 2023
+++ othersrc/external/bsd/elex/dist/gap.c	Wed Jun 21 23:36:17 2023
@@ -0,0 +1,1174 @@
+/*-
+ * Copyright (c) 2023 Alistair Crooks <a...@netbsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "agcre.h"
+#include "gap.h"
+
+#define X	0
+#define Y	1
+
+/* maximum screen dimensions and parameters */
+#define MAXROWS			8192
+#define MAXCOLS			16384
+#define MAXTABSIZE		16
+#define DEFAULT_TABSIZE		8
+
+/* search params */
+typedef struct reparams_t {
+	int			reverse;	/* search backwards */
+	int			eofstop;	/* stop at eof - no wrap */
+	int			range;		/* range in regmatch_t args */
+	int			lineonly;	/* terminate search at the end of line */
+	uint32_t		flags;		/* regex flags */
+	uint64_t		lhs;		/* saved lhs position */
+	uint64_t		lhsnl;		/* saved lhs line position */
+} reparams_t;
+
+/* line information */
+typedef struct eline_t {
+	uint64_t		 len;		/* line length in bytes */
+	uint64_t		 state;		/* start state for syntax coloring */
+	uint64_t		 width;		/* detabbed length of line */
+} eline_t;
+
+/* buffer gap structure */
+struct gap_t {
+	char			*v;		/* buffer gap text */
+	uint64_t		 size;		/* size of allocated text */
+	uint64_t		 lhs;		/* # of bytes on lhs */
+	uint64_t		 lhsnl;		/* # of \n on lhs */
+	uint64_t		 rhs;		/* # of bytes on rhs */
+	uint64_t		 rhsnl;		/* # of \n on rhs */
+	eline_t			*line;		/* counts per line */
+	uint64_t		 linealloc;	/* # of lines allocated */
+	char			*filename;	/* any associated filename */
+	uint64_t		 chgc;		/* # of changes made */
+	/* regex part */
+	agcre_regex_t		 r;		/* regex for searching */
+	uint64_t		 msize;		/* allocated size of mv array */
+	agcre_regmatch_t	*mv;		/* match values */
+	int			 compiled;	/* regex was previously compiled */
+	/* screen handling part */
+	uint64_t		 tabc;		/* # of tabs in the buffer */
+	uint64_t		 tabsize;	/* # of spaces to expand a tab */
+
+	uint64_t		 windim[2];	/* window dimensions */
+	uint64_t		 origin;	/* on-screen start offset */
+	uint64_t		 last;		/* on-screen end offset */
+	uint64_t		 offM;		/* mid-screen offset */
+	uint64_t		 offL;		/* last line offset */
+	int			 repaint;	/* need to repaint screen */
+};
+
+/* find the index on the rhs */
+static inline uint64_t
+rhssub(uint64_t n, uint64_t size)
+{
+	return size - 1 - n;
+}
+
+/* count the ch characters in len bytes of v */
+static inline uint64_t
+count(const char *v, uint8_t ch, uint64_t len)
+{
+	uint64_t	 nl;
+	uint64_t	 i;
+
+	for (nl = 0, i = 0 ; i < len ; i++) {
+		if (v[i] == ch) {
+			nl += 1;
+		}
+	}
+	return nl;
+}
+
+/* return the width of a byte at current column */
+static inline uint64_t
+bytewidth(gap_t *gap, uint64_t col, char ch)
+{
+	switch(ch) {
+	case '\n':
+		return gap->windim[X] - col;
+	case '\t':
+		return ((gap->tabsize - 1) - (col % gap->tabsize)) + 1;
+	default:
+		return 1;
+	}
+}
+
+static const uint32_t	maxlen = 32;
+
+/* hashing function */
+static uint32_t
+djbhash(const char *s)
+{
+	const char	*from = s;
+	uint32_t	 hash;
+
+	for (hash = 5381; *s && (uint32_t)(s - from) < maxlen ; ) {
+		hash = hash * 33 + *s++;
+	}
+	return hash + (hash >> 5);
+}
+
+/* calculate line lengths */
+static int
+calclens(gap_t *gap)
+{
+	uint64_t	 len;
+	uint64_t	 col;
+	uint64_t	 nl;
+	eline_t		*line;
+	char		*p;
+
+	if (gap->lhsnl + gap->rhsnl  + 8 > gap->linealloc) {
+		nl = howmany(gap->lhsnl + gap->rhsnl, 1024) * 1024;
+		if ((line = realloc(gap->line, sizeof(*line) * nl)) == NULL) {
+			return 0;
+		}
+		memset(&line[gap->linealloc], 0x0, (nl - gap->linealloc) * sizeof(*line));
+		gap->line = line;
+		gap->linealloc = nl;
+	}
+	for (nl = 0, col = 0, len = 0, p = gap->v ; (uint64_t)(p - gap->v) < gap->lhs ; p++) {
+		len += 1;
+		switch(*p) {
+		case '\n':
+			gap->line[nl].width = col;
+			gap->line[nl].len = len;
+			len = 0;
+			col = 0;
+			nl += 1;
+			break;
+		case '\t':
+			col += ((gap->tabsize - 1) - (col % gap->tabsize)) + 1;
+			break;
+		default:
+			col += 1;
+			break;
+		}
+	}
+	for (p = &gap->v[rhssub(gap->rhs, gap->size)]; (uint64_t)(p - gap->v) < gap->size ; p++) {
+		len += 1;
+		switch(*p) {
+		case '\n':
+			gap->line[nl].width = col;
+			gap->line[nl].len = len;
+			len = 0;
+			col = 0;
+			nl += 1;
+			break;
+		case '\t':
+			col += ((gap->tabsize - 1) - (col % gap->tabsize)) + 1;
+			break;
+		default:
+			col += 1;
+			break;
+		}
+	}
+	gap->line[nl].width = col;
+	gap->line[nl].len = len;
+	return 1;
+}
+
+/********************************************************/
+
+/* ensure we can handle an extra add bytes, rounded up to nearest 1K */
+static int
+canhandle(gap_t *gap, uint64_t add)
+{
+	uint64_t	 newsize;
+	char		*v;
+
+	if (gap->lhs + gap->rhs + 10 + add > gap->size) {
+		newsize = howmany(gap->size + add, 1024) * 1024; 
+		if ((v = calloc(1, newsize)) == NULL) {
+			return 0;
+		}
+		memcpy(v, gap->v, gap->lhs);
+		memcpy(&v[rhssub(gap->rhs, newsize)], &gap->v[rhssub(gap->rhs, gap->size)], gap->rhs);
+		gap->size = newsize;
+		free(gap->v);
+		gap->v = v;
+	}
+	return 1;
+}
+
+/* insert text at the end of the lhs in the buffer */
+static int
+insert(gap_t *gap, const char *s, uint64_t len)
+{
+	if (!canhandle(gap, len + 10)) {
+		return 0;
+	}
+	if (s == NULL) {
+		memset(&gap->v[gap->lhs], 0x0, len);
+		gap->lhs += len;
+	} else {
+		memcpy(&gap->v[gap->lhs], s, len);
+		gap->lhs += len;
+		gap->lhsnl += count(s, '\n', len);
+		gap->tabc += count(s, '\t', len);
+	}
+	gap->chgc += 1;
+	return calclens(gap);
+}
+
+/* delete text from the rhs of the gap */
+static int
+delete(gap_t *gap, uint64_t n)
+{
+	if (n > gap->rhs) {
+		return 0;
+	}
+	gap->rhsnl -= count(&gap->v[rhssub(gap->rhs, gap->size)], '\n', n);
+	gap->tabc -= count(&gap->v[rhssub(gap->rhs, gap->size)], '\t', n);
+	gap->rhs -= n;
+	gap->chgc += 1;
+	return calclens(gap);
+}
+
+/* move n bytes left */
+static int
+moveleft(gap_t *gap, uint64_t n)
+{
+	uint64_t	nl;
+
+	if (n > gap->lhs) {
+		return 0;
+	}
+	nl = count(&gap->v[gap->lhs - n], '\n', n);
+	memmove(&gap->v[rhssub(gap->rhs + n, gap->size)], &gap->v[gap->lhs - n], n);
+	gap->lhs -= n;
+	gap->rhs += n;
+	gap->lhsnl -= nl;
+	gap->rhsnl += nl;
+	return 1;
+}
+
+/* move n bytes right */
+static int
+moveright(gap_t *gap, uint64_t n)
+{
+	uint64_t	nl;
+
+	if (n > gap->rhs) {
+		return 0;
+	}
+	nl = count(&gap->v[rhssub(gap->rhs, gap->size)], '\n', n);
+	memmove(&gap->v[gap->lhs], &gap->v[rhssub(gap->rhs, gap->size)], n);
+	gap->lhs += n;
+	gap->rhs -= n;
+	gap->lhsnl += nl;
+	gap->rhsnl -= nl;
+	return 1;
+}
+
+/* calculate the offset for the start of the line */
+static uint64_t
+line2off(gap_t *gap, uint64_t lineno)
+{
+	uint64_t	off;
+	uint64_t	i;
+
+	for (off = 0, i = 0 ; i < lineno && i < gap->lhsnl + gap->rhsnl ; i++) {
+		off += gap->line[i].len;
+	}
+	return off;
+}
+
+/* calculate the offset for the start of the line */
+static uint64_t
+off2line(gap_t *gap, uint64_t off)
+{
+	uint64_t	cc;
+	uint64_t	i;
+
+	for (cc = 0, i = 0 ; cc < off && i < gap->lhsnl + gap->rhsnl ; i++) {
+		cc += gap->line[i].len;
+	}
+	return i;
+}
+
+/* move to an absolute line number */
+static int
+move2line(gap_t *gap, uint64_t n)
+{
+	uint64_t	 off;
+
+	if (n > gap->lhsnl + gap->rhsnl) {
+		return 0;
+	}
+	if ((off = line2off(gap, n)) < gap->lhs) {
+		return moveleft(gap, gap->lhs - off);
+	}
+	return moveright(gap, off - gap->lhs);
+}
+
+/* move to an absolute offset in the file */
+static int
+move2offset(gap_t *gap, uint64_t off)
+{
+	if (off > gap->lhs + gap->rhs) {
+		return 0;
+	}
+	if (off > gap->lhs) {
+		return moveright(gap, off - gap->lhs);
+	}
+	if (off < gap->lhs) {
+		return moveleft(gap, gap->lhs - off);
+	}
+	return 1;
+}
+
+/* return the detabbed offset in the line */
+static uint64_t
+line_detab_offset(gap_t *gap)
+{
+	uint64_t	col;
+	uint64_t	i;
+
+	if (gap->tabsize == 0) {
+		return 0;
+	}
+	for (i = line2off(gap, gap->lhsnl), col = 0 ; i < gap->lhs ; i++) {
+		col += bytewidth(gap, col, gap->v[i]);
+	}
+	return col;
+}
+
+/* return the current screen offset */
+static uint64_t
+screen_offset(gap_t *gap)
+{
+	uint64_t	off;
+	uint64_t	i;
+
+	if (gap->windim[X] == 0) {
+		return 0;
+	}
+	for (off = 0, i = off2line(gap, gap->origin); i < gap->lhsnl ; i++) {
+		off += howmany(gap->line[i].width, gap->windim[X]) * gap->windim[X];
+	}
+	return off + line_detab_offset(gap);
+}
+
+/* move n cols right */
+static int
+movecolright(gap_t *gap, uint64_t n)
+{
+	uint64_t	 col;
+
+	if (n > gap->rhs) {
+		return 0;
+	}
+	/* still might overshoot */
+	for (col = line_detab_offset(gap) ; col < n ; ) {
+		col += bytewidth(gap, col, gap->v[rhssub(gap->rhs, gap->size)]);
+		if (!moveright(gap, 1)) {
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/* read a file, and insert it into the gap */
+static int
+readfile(gap_t *gap, const char *file)
+{
+	struct stat	 st;
+	uint64_t	 bytes;
+	size_t		 cc;
+	size_t		 rc;
+	FILE		*fp;
+	char		*v;
+	int		 allocated;
+	int		 ok;
+
+	if ((fp = fopen(file, "r")) == NULL) {
+		return 0;
+	}
+	fstat(fileno(fp), &st);
+	bytes = st.st_size;
+	allocated = 0;
+	if (gap->filename == NULL) {
+		cc = strlen(file);
+		allocated = 1;
+		if ((gap->filename = calloc(1, cc + 1)) == NULL) {
+			fclose(fp);
+			return 0;
+		}
+		memcpy(gap->filename, file, cc);
+	}
+	if ((v = calloc(1, bytes)) == NULL) {
+		if (allocated) {
+			free(gap->filename);
+			gap->filename = NULL;
+		}
+		fclose(fp);
+		return 0;
+	}
+	for (cc = 0 ; cc < bytes ; cc += rc) {
+		if ((rc = fread(&v[cc], 1, bytes - cc, fp)) == 0) {
+			break;
+		}
+	}
+	fclose(fp);
+	ok = insert(gap, v, bytes);
+	free(v);
+	if (!ok) {
+		return 0;
+	}
+	return moveleft(gap, bytes);
+}
+
+/* make a filename template */
+static int
+make_template(char *name, size_t size, const char *base)
+{
+	struct tm	 tm;
+	time_t		 t;
+
+	t = time(NULL);
+	localtime_r(&t, &tm);
+	snprintf(name, size, "%s-%.04d%02d%02d.%02d%02d%02d",
+		base,
+		tm.tm_year + 1900,
+		tm.tm_mon + 1,
+		tm.tm_mday,
+		tm.tm_hour,
+		tm.tm_min,
+		tm.tm_sec);
+	return 1;
+}
+
+/* write file to persistent storage */
+static int
+writefile(gap_t *gap, const char *filename)
+{
+	size_t	 cc;
+	size_t	 wc;
+	FILE	*fp;
+	char	 name[1024];
+	int	 fd;
+
+	make_template(name, sizeof(name), (filename) ? filename : (gap->filename) ? gap->filename : "Untitled");
+	if ((fd = mkstemp(name)) < 0) {
+		return 0;
+	}
+	fp = fdopen(fd, "w+");
+	for (cc = 0 ; cc < gap->lhs ; cc += wc) {
+		if ((wc = fwrite(&gap->v[cc], 1, gap->lhs - cc, fp)) == 0) {
+			goto writefile_error;
+		}
+	}
+	for (cc = 0 ; cc < gap->rhs ; cc += wc) {
+		if ((wc = fwrite(&gap->v[rhssub(gap->rhs - cc, gap->size)], 1, gap->rhs - cc, fp)) == 0) {
+			goto writefile_error;
+		}
+	}
+	fclose(fp);
+	if (filename == NULL && gap->filename != NULL) {
+		/* need to rename file */
+		if (rename(name, gap->filename) != 0) {
+			unlink(name);
+			return 0;
+		}
+	}
+	return 1;
+writefile_error:
+	fclose(fp);
+	unlink(name);
+	return 0;
+}
+
+/* allocate storage, user frees */
+static char *
+allocate(const char *s, uint64_t n)
+{
+	char	*v;
+
+	if ((v = calloc(1, n)) == NULL) {
+		return NULL;
+	}
+	memcpy(v, s, n);
+	return v;
+}
+
+/* search for a regular expression in the gap */
+static int
+find_regex(gap_t *gap, const char *pat, reparams_t *params, int rhs, uint64_t from, uint64_t to)
+{
+	agcre_regmatch_t	*newmv;
+	const char		*v;
+
+	if (gap->compiled) {
+		agcre_regfree(&gap->r);
+		gap->compiled = 0;
+	}
+	if (agcre_regcomp(&gap->r, pat, params->flags) != 0) {
+		return 0;
+	}
+	gap->compiled = 1;
+	if (gap->r.re_nsub + 1 > gap->msize) {
+		newmv = realloc(gap->mv, (gap->r.re_nsub + 10) * sizeof(*newmv));
+		if (newmv == NULL) {
+			return 0;
+		}
+		gap->mv = newmv;
+		gap->msize = gap->r.re_nsub + 10;
+	}
+	v = (rhs) ? &gap->v[rhssub(gap->rhs, gap->size)] : gap->v;
+	if (params->reverse) {
+		gap->mv[0].rm_so = from;
+		gap->mv[0].rm_eo = to;
+		if (agcre_rev_regexec(&gap->r, v, gap->msize, gap->mv, AGCRE_REG_STARTEND) != 0) {
+			return 0;
+		}
+	} else {
+		gap->mv[0].rm_so = from;
+		gap->mv[0].rm_eo = to;
+		if (agcre_regexec(&gap->r, v, gap->msize, gap->mv, AGCRE_REG_STARTEND) != 0) {
+			return 0;
+		}
+	}
+	return (rhs) ? move2offset(gap, gap->lhs + gap->mv[0].rm_so) : move2offset(gap, gap->mv[0].rm_so);
+}
+
+/* calculate a percentage as an integer */
+static inline uint32_t
+percentage(uint64_t num, uint64_t denom)
+{
+	return (denom == 0) ? 0 : (int)(num * (uint64_t)100) / denom;
+}
+
+/* allocate a line and copy contents in without disturbing the buffer */
+static char *
+allocline(gap_t *gap, uint64_t lineno, uint64_t *size)
+{
+	uint64_t	 off;
+	uint64_t	 len;
+	uint64_t	 cc;
+	char		*s;
+
+	if (lineno > gap->lhsnl + gap->rhsnl) {
+		return NULL;
+	}
+	len = gap->line[lineno].len;
+	off = line2off(gap, lineno);
+	if (off > gap->lhs) {
+		/* all on rhs */
+		return allocate(&gap->v[rhssub(gap->rhs - (off - gap->lhs), gap->size)], *size = len);
+	}
+	if (off + len < gap->lhs) {
+		/* all on lhs */
+		return allocate(&gap->v[off], *size = len);
+	}
+	/* a bitta both */
+	if ((s = calloc(1, *size = gap->line[lineno].len)) == NULL) {
+		return NULL;
+	}
+	memcpy(s, &gap->v[off], cc = gap->lhs - off);
+	memcpy(&s[cc], &gap->v[rhssub(gap->rhs, gap->size)], len);
+	return s;
+}
+
+/* clone the original gap */
+static int
+clonegap(gap_t *gap, const gap_t *orig)
+{
+	eline_t		*line;
+	char		*v;
+
+	if (gap->linealloc < orig->linealloc) {
+		if ((line = realloc(gap->line, sizeof(*line) * orig->linealloc)) == NULL) {
+			return 0;
+		}
+		gap->line = line;
+		gap->linealloc = orig->linealloc;
+	}
+	if (gap->size < orig->size) {
+		if ((v = realloc(gap->v, orig->size)) == NULL) {
+			return 0;
+		}
+		gap->v = v;
+		gap->size = orig->size;
+	}
+	/* copy these carefully, they may be a different size */
+	memcpy(gap->v, orig->v, orig->lhs);
+	memcpy(&gap->v[rhssub(orig->rhs, gap->size)], &orig->v[rhssub(orig->rhs, orig->size)], orig->rhs);
+	memcpy(gap->line, orig->line, orig->linealloc * sizeof(*gap->line));
+	gap->lhs = orig->lhs;
+	gap->lhsnl = orig->lhsnl;
+	gap->rhs = orig->rhs;
+	gap->rhsnl = orig->rhsnl;
+	gap->tabc = orig->tabc;
+	gap->chgc = orig->chgc;
+	return 1;
+}
+
+/* convert all tabs to spaces */
+static char *
+detab(gap_t *gap, uint64_t *size)
+{
+	uint64_t	 width;
+	uint32_t	 col;
+	char		*o;
+	char		*v;
+	char		*out;
+
+	if (gap->tabsize == 0) {
+		return NULL;
+	}
+	if ((out = calloc(1, gap->size + (gap->tabc * gap->tabsize))) == NULL) {
+		return NULL;
+	}
+	move2line(gap, gap->lhsnl);
+	v = &gap->v[rhssub(gap->rhs, gap->size)];
+	for (col = 0, o = out ; (uint64_t)(v - gap->v) < gap->size ; v++) {
+		switch(*v) {
+		case '\n':
+			*o++ = '\n';
+			col = 0;
+			break;
+		case '\t':
+			width = ((gap->tabsize - 1) - (col % gap->tabsize)) + 1;
+			memset(o, ' ', width);
+			o += width;
+			col += width;
+			break;
+		default:
+			*o++ = *v;
+			col += 1;
+			break;
+		}
+	}
+	*size = (uint64_t)(o - out);
+	return out;
+}
+
+/* return the whole buffer as a string */
+static char *
+getbuffer(gap_t *gap, uint64_t *size)
+{
+	char	*s;
+
+	if ((s = calloc(1, gap->lhs + gap->rhs + 1)) == NULL) {
+		return NULL;
+	}
+	if (gap->lhs > 0) {
+		memcpy(s, gap->v, gap->lhs);
+	}
+	if (gap->rhs > 0) {
+		memcpy(&s[gap->lhs], &gap->v[rhssub(gap->rhs, gap->size)], gap->rhs);
+	}
+	*size = gap->lhs + gap->rhs;
+	return s;
+}
+
+/* given an origin, calulate the last offset on screen */
+static int
+calclast(gap_t *gap, uint64_t origin, uint64_t originnl)
+{
+	uint64_t	 x;
+	uint64_t	 y;
+	char		*rhs;
+	char		*p;
+
+	gap->origin = gap->offM = gap->offL = origin;
+	p = rhs = &gap->v[rhssub(gap->rhs, gap->size)];
+	for (x = y = 0 ; (uint64_t)(p - rhs) < gap->rhs && y < gap->windim[Y] ; p++) {
+		x += bytewidth(gap, x, *p);
+		if (x >= gap->windim[X]) {
+			x = 0;
+			y += 1;
+			if (y == (gap->windim[Y] / 2) - 1) {
+				gap->offM = origin + (uint64_t)(p - rhs);
+			} else if (y == gap->windim[Y] - 1) {
+				gap->offL = origin + (uint64_t)(p - rhs);
+			}
+		}
+	}
+	gap->last = origin + (uint64_t)(p - rhs);
+	return 1;
+}
+
+/* refocus the origin, and calculate last */
+static int
+refocus(gap_t *gap, int force)
+{
+	uint64_t	 was;
+	uint64_t	 i;
+
+	if (force || gap->lhs < gap->origin || gap->lhs > gap->last) {
+		was = gap->lhs;
+		moveleft(gap, gap->lhs - line2off(gap, gap->lhsnl));
+		for (i = gap->lhs ; i < gap->lhs + gap->rhs ; i++) {
+			calclast(gap, gap->lhs, gap->lhsnl);
+			if (was >= gap->origin && was <= gap->last) {
+				break;
+			}
+			movecolright(gap, gap->windim[X]);
+		}
+		move2offset(gap, was);
+	}
+	return 1;
+}
+
+/* return the window's text as a string */
+static char *
+get_window_text(gap_t *gap, uint64_t *size)
+{
+	uint64_t	 linelen;
+	uint64_t	 buflen;
+	uint64_t	 i;
+	uint64_t	 y;
+	char		*s;
+
+	if (gap->windim[X] == 0 || gap->windim[Y] == 0 || gap->tabsize == 0) {
+		return 0;
+	}
+	refocus(gap, gap->repaint);
+	for (buflen = 0, y = 0, i = 0 ; i < gap->rhsnl && y < gap->windim[Y] ; i++) {
+		linelen = gap->line[gap->lhsnl + i].len;
+		y += (linelen / gap->windim[Y]) + 1;
+		buflen += linelen;
+	}
+	if ((s = calloc(1, buflen)) == NULL) {
+		return NULL;
+	}
+	memcpy(s, &gap->v[rhssub(gap->rhs, gap->size)], *size = buflen);
+	return s;
+}
+
+/* do the equivalent of memset */
+static uint64_t
+memset_in_gap(gap_t *gap, const uint8_t *p, uint64_t len)
+{
+	uint64_t	 tab;
+	uint64_t	 nl;
+	char		*rhs;
+
+	if (p == NULL || len > gap->rhs) {
+		return 0;
+	}
+	if (len > 0) {
+		rhs = &gap->v[rhssub(gap->rhs, gap->size)];
+		nl = count(rhs, '\n', len);
+		tab = count(rhs, '\t', len);
+		gap->rhsnl -= nl;
+		gap->tabc -= tab;
+		switch(*p) {
+		case '\t':
+			gap->tabc += len;
+			break;
+		case '\n':
+			gap->rhsnl += len;
+			break;
+		}
+		memset(rhs, *p, len);
+		moveright(gap, len);
+		gap->chgc += 1;
+	}
+	return 1;
+}
+
+/* do an memchr forwards (to the right) */
+static int
+memchr_in_gap(gap_t *gap, const char *s, uint64_t n)
+{
+	uint64_t	 c;
+	uint64_t	 i;
+	char		*p;
+
+	if (gap->rhs > 0) {
+		for (c = 0, i = 0, p = &gap->v[rhssub(gap->rhs, gap->size)] ; i < gap->rhs ; i++) {
+			if (*p++ == *s) {
+				if (++c == n) {
+					return moveright(gap, i);
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+/* do an memrchr backwards (to the left) */
+static int
+memrchr_in_gap(gap_t *gap, const char *s, uint64_t n)
+{
+	uint64_t	 c;
+	uint64_t	 i;
+	char		*p;
+
+	if (gap->lhs > 0) {
+		for (c = 0, i = 0, p = &gap->v[gap->lhs - 1] ; i < gap->lhs ; i++) {
+			if (*p-- == *s) {
+				if (++c == n) {
+					return moveleft(gap, i);
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+/* return from rhs to eol */
+static char *
+rhs_to_eol(gap_t *gap, uint64_t *size)
+{
+	uint64_t	cc;
+
+	cc = (gap->rhsnl == 0) ? gap->rhs : line2off(gap, gap->lhsnl + 1) - gap->lhs;
+	return allocate(&gap->v[rhssub(gap->rhs, gap->size)], *size = cc);
+}
+
+/* move to logical beginning of line */
+static int
+lbol(gap_t *gap)
+{
+	static agcre_regex_t	r;
+	agcre_regmatch_t	mv[2];
+	static int		compiled;
+	uint64_t		off;
+
+	if (!compiled) {
+		agcre_regcomp(&r, "\\S", AGCRE_REG_EXTENDED);
+		compiled = 1;
+	}
+	if ((off = gap->lhs - line2off(gap, gap->lhsnl)) > 0) {
+		moveleft(gap, off);
+	}
+	mv[0].rm_so = 0;
+	mv[0].rm_eo = gap->line[gap->lhsnl].len;
+	if (agcre_regexec(&r, &gap->v[rhssub(gap->rhs, gap->size)], 2, mv, AGCRE_REG_STARTEND) == 0) {
+		moveright(gap, mv[0].rm_so);
+	}
+	return 1;
+}
+
+/* delete n lines */
+static int
+delete_lines(gap_t *gap, uint64_t n)
+{
+	uint64_t	cc;
+
+	if (n >= gap->rhsnl) {
+		return 0;
+	}
+	move2line(gap, gap->lhsnl);
+	cc = line2off(gap, gap->lhsnl + n);
+	return delete(gap, cc);
+}
+
+/* yank n lines */
+static char *
+yank_lines(gap_t *gap, uint64_t n, uint64_t *size)
+{
+	uint64_t	cc;
+
+	if (n >= gap->rhsnl) {
+		return 0;
+	}
+	move2line(gap, gap->lhsnl);
+	cc = line2off(gap, gap->lhsnl + n);
+	return allocate(&gap->v[rhssub(gap->rhs, gap->size)], *size = cc);
+}
+
+/********************************************************/
+
+/* new buffer gap */
+gap_t *
+gap_new(void)
+{
+	uint64_t	 n;
+	gap_t		*gap;
+
+	if ((gap = calloc(1, sizeof(*gap))) == NULL) {
+		return NULL;
+	}
+	n = howmany(8192, 1024) * 1024;
+	if ((gap->v = calloc(1, n)) == NULL) {
+		free(gap);
+		return NULL;
+	}
+	gap->size = n;
+	gap->tabsize = DEFAULT_TABSIZE;
+	return gap;
+}
+
+/* dispose of the gap */
+int
+gap_dispose(gap_t **gap)
+{
+	if (gap && *gap) {
+		free((*gap)->v);
+		free(*gap);
+		*gap = NULL;
+		return 1;
+	}
+	return 0;
+}
+
+/* execute an action, return an int */
+uint64_t
+gap_exec(gap_t *gap, const char *action, const void *vs, uint64_t n)
+{
+	if (gap == NULL || action == NULL) {
+		return 0;
+	}
+	switch(djbhash(action)) {
+	case /* "bytec" */ 0xfaa3bed:
+		return gap->lhs + gap->rhs;
+	case /* "changec" */ 0xc848b36a:
+		return gap->chgc;
+	case /* "clone" */ 0xfb57683:
+		return (vs == NULL) ? 0 : clonegap(gap, vs);
+	case /* "delete" */ 0x47a09b:
+		return delete(gap, n);
+	case /* "delete-lines" */ 0xa11ab5d3:
+		return delete_lines(gap, n);
+	case /* "get-cols" */ 0x2be06e6:
+		return gap->windim[X];
+	case /* "get-last" */ 0x2c2e14f:
+		return gap->last;
+	case /* "get-L" */ 0xffc336c:
+		return gap->offL;
+	case /* "get-M" */ 0xffc336d:
+		return gap->offM;
+	case /* "get-origin" */ 0x6f6f52d9:
+		return gap->origin;
+	case /* "get-rows" */ 0x2c683d8:
+		return gap->windim[Y];
+	case /* "get-tabsize" */ 0x48e3315e:
+		return gap->tabsize;
+	case /* "insert" */ 0x4faa2ae:
+		return insert(gap, vs, n);
+	case /* "lhs" */ 0xbe4d08d:
+		return gap->lhs;
+	case /* "lhsnl" */ 0x105b3874:
+		return gap->lhsnl;
+	case /* "linec" */ 0x105bb20d:
+		return gap->lhsnl + gap->rhsnl;
+	case /* "line-lhs" */ 0xc80137d5:
+		return gap->lhs - line2off(gap, gap->lhsnl);
+	case /* "line-lhs-detab" */ 0x1a300162:
+		return line_detab_offset(gap);
+	case /* "line-rhs" */ 0xc8015227:
+		return (gap->rhsnl == 0) ? gap->rhs : line2off(gap, gap->lhsnl + 1) - gap->lhs;
+	case /* "line-to-offset" */ 0xc6c7d98a:
+		return line2off(gap, n);
+	case /* "linelength" */ 0x9c3bc7ba:
+		return (n >= gap->lhsnl + gap->rhsnl) ? 0 : gap->line[n].len;
+	case /* "linewidth" */ 0x58b5d1f:
+		return (n >= gap->lhsnl + gap->rhsnl) ? 0 : gap->line[n].width;
+	case /* "memchr" */ 0xdee8825:
+		return (vs == NULL) ? 0 : memchr_in_gap(gap, vs, n);
+	case /* "memrchr" */ 0xc3c7f174:
+		return (vs == NULL) ? 0 : memrchr_in_gap(gap, vs, n);
+	case /* "memset" */ 0xdeecdf1:
+		return memset_in_gap(gap, vs, n);
+	case /* "move-lbol" */ 0x693036fc:
+		return lbol(gap);
+	case /* "move-left" */ 0x693042fb:
+		return moveleft(gap, n);
+	case /* "move-left-line" */ 0xb22440bb:
+		return move2line(gap, gap->lhsnl - n);
+	case /* "move-right" */ 0x27aae060:
+		return moveright(gap, n);
+	case /* "move-right-line" */ 0x216f6cbe:
+		return move2line(gap, gap->lhsnl + n);
+	case /* "move-to-bof" */ 0x225a0d4:
+		return moveleft(gap, gap->lhs);
+	case /* "move-to-eof" */ 0x225adfd:
+		return moveright(gap, gap->rhs);
+	case /* "move-to-eol" */ 0x225ae03:
+		return moveright(gap,
+			(gap->rhsnl == 0) ? gap->rhs : line2off(gap, gap->lhsnl + 1) - gap->lhs - 2);
+	case /* "move-to-line" */ 0x46df4a33:
+		return move2line(gap, n);
+	case /* "move-to-offset" */ 0x62d5e227:
+		return move2offset(gap, n);
+	case /* "offset-to-line" */ 0x9b4a6ad8:
+		return off2line(gap, n);
+	case /* "read-file" */ 0x1ff4ae6f:
+		return (vs == NULL) ? 0 : readfile(gap, vs);
+	case /* "refocus" */ 0x402de943:
+		return refocus(gap, 0);
+	case /* "reset-changec" */ 0xa9b38cdf:
+		gap->chgc = 0;
+		return 1;
+	case /* "rhs" */ 0xbe4eadf:
+		return gap->rhs;
+	case /* "rhsnl" */ 0x10cb3012:
+		return gap->rhsnl;
+	case /* "screen-offset" */ 0xa4877a9:
+		return screen_offset(gap);
+	case /* "set-cols" */ 0xe6cb146:
+		if (n >= MAXCOLS) {
+			return 0;
+		}
+		gap->windim[X] = n;
+		return 1;
+	case /* "set-origin" */ 0xa17216d2:
+		if (n >= gap->lhs + gap->rhs) {
+			return 0;
+		}
+		gap->origin = n;
+		gap->repaint = 1;
+		return 1;
+	case /* "set-rows" */ 0xe752e39:
+		if (n >= MAXROWS) {
+			return 0;
+		}
+		gap->windim[Y] = n;
+		return 1;
+	case /* "set-tabsize" */ 0x8b3e7462:
+		if (n == 0 || n > MAXTABSIZE) {
+			return 0;
+		}
+		gap->tabsize = n;
+		return 1;
+	case /* "version" */ 0x76986fad:
+		return GAP_H_;
+	case /* "wipe" */ 0x80851f0a:
+		gap->lhs = gap->rhs = gap->lhsnl = gap->rhsnl = gap->chgc = 0;
+		return 1;
+	case /* "write-file" */ 0xb2d1c629:
+		return writefile(gap, vs);
+	}
+	return 0;
+}
+
+/* used to stringify the namespace we're using */
+#define GAP_STRINGIFY(_x)	GAP_STRINGIFY2(_x)
+#define GAP_STRINGIFY2(_x)	#_x
+
+/* execute an action on the gap, return allocated byte string */
+char *
+gap_exec_mem(gap_t *gap, const char *action, uint64_t n, uint64_t *size)
+{
+	if (gap == NULL || action == NULL || size == NULL) {
+		return 0;
+	}
+	*size = 0;
+	switch (djbhash(action)) {
+	case /* "detab" */ 0xfc43e9e:
+		return detab(gap, size);
+	case /* "filename" */ 0x357ed0f9:
+		return (gap->filename == NULL) ? NULL :
+			allocate(gap->filename, *size = strlen(gap->filename));
+	case /* "get-rhs-eol" */ 0xc40b665a:
+		return rhs_to_eol(gap, size);
+	case /* "getbuf" */ 0x7838f68:
+		return getbuffer(gap, size);
+	case /* "getlhs" */ 0x783b999:
+		return allocate(gap->v, *size = gap->lhs);
+	case /* "getline" */ 0xf7faf0e6:
+		return allocline(gap, n, size);
+	case /* "getrhs" */ 0x783d3eb:
+		return allocate(&gap->v[rhssub(gap->rhs, gap->size)], *size = gap->rhs);
+	case /* "get-window-text" */ 0xb9d5d262:
+		return get_window_text(gap, size);
+	case /* "namespace" */ 0x41041c23:
+		return allocate(GAP_STRINGIFY(LIB_NAMESPACE),
+			*size = strlen(GAP_STRINGIFY(LIB_NAMESPACE)));
+	case /* "yank-lines" */ 0x66a66734:
+		return yank_lines(gap, n, size);
+	}
+	return NULL;
+}
+
+#define ARG_LINE_LHS	2
+#define ARG_LINE_RHS	3
+#define ARG_LHS		4
+#define ARG_RHS		5
+#define ARG_ICASE	6
+#define ARG_REVERSE	7
+
+/* search for a regexp */
+int
+gap_search(gap_t *gap, const char *args, const char *pat, uint64_t mc, void *vmv)
+{
+	static agcre_regex_t	 arg;
+	agcre_regmatch_t	*mv = (agcre_regmatch_t *)vmv;
+	agcre_regmatch_t	 m[10];
+	static int		 compiled;
+	reparams_t		 params;
+	uint64_t		 from;
+	uint64_t		 to;
+	int			 rhs;
+
+	if (gap == NULL || args == NULL || pat == NULL || mc == 0 || mv == NULL) {
+		return 0;
+	}
+	if (!compiled) {
+		agcre_regcomp(&arg, "((linelhs)|(linerhs)|(lhs)|(rhs))(,icase)?(,reverse)?", AGCRE_REG_EXTENDED);
+		/*                   12       2 3       3 4   4 5   516      6 7        7 */
+		compiled = 1;
+	}
+	memset(&params, 0x0, sizeof(params));
+	params.flags = AGCRE_REG_EXTENDED;
+	params.lhs = gap->lhs;
+	params.lhsnl = gap->lhsnl;
+	if (args) {
+		if (agcre_regexec(&arg, args, __arraycount(m), m, 0) != 0) {
+			return 0;
+		}
+		if (m[ARG_LINE_LHS].rm_so >= 0) {
+			rhs = 0;
+			from = line2off(gap, gap->lhsnl);
+			to = gap->lhs - from;
+		} else if (m[ARG_LINE_RHS].rm_so >= 0) {
+			rhs = 1;
+			from = 1;
+			to = gap_exec(gap, "line-rhs", NULL, 0);
+		} else if (m[ARG_LHS].rm_so >= 0) {
+			rhs = 0;
+			from = 0;
+			to = gap->lhs;
+		} else if (m[ARG_RHS].rm_so >= 0) {
+			rhs = 1;
+			from = 1;
+			to = gap->rhs;
+		}
+		if (m[ARG_ICASE].rm_so >= 0) {
+			params.flags |= AGCRE_REG_ICASE;
+		}
+		if (m[ARG_REVERSE].rm_so >= 0) {
+			params.reverse = 1;
+		}
+	}
+	return find_regex(gap, pat, &params, rhs, from, to);
+}
Index: othersrc/external/bsd/elex/dist/gap.h
diff -u /dev/null othersrc/external/bsd/elex/dist/gap.h:1.1
--- /dev/null	Wed Jun 21 23:36:17 2023
+++ othersrc/external/bsd/elex/dist/gap.h	Wed Jun 21 23:36:17 2023
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 2023 Alistair Crooks <a...@netbsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef GAP_H_
+#define GAP_H_	20230303
+
+#include <inttypes.h>
+
+#ifdef LIB_NAMESPACE
+#define GAP_CONCAT(x, y)	x##y
+#define GAP_NAMESPACE(x, y)	GAP_CONCAT(x, y)
+#define gap_t			GAP_NAMESPACE(LIB_NAMESPACE, gap_t)
+#define gap_new			GAP_NAMESPACE(LIB_NAMESPACE, gap_new)
+#define gap_dispose		GAP_NAMESPACE(LIB_NAMESPACE, gap_dispose)
+#define gap_exec		GAP_NAMESPACE(LIB_NAMESPACE, gap_exec)
+#define gap_exec_mem		GAP_NAMESPACE(LIB_NAMESPACE, gap_exec_mem)
+#define gap_search		GAP_NAMESPACE(LIB_NAMESPACE, gap_search)
+#endif
+
+struct gap_t;
+typedef struct gap_t	gap_t;
+
+#ifndef __BEGIN_DECLS
+#  if defined(__cplusplus)
+#  define __BEGIN_DECLS           extern "C" {
+#  define __END_DECLS             }
+#  else
+#  define __BEGIN_DECLS
+#  define __END_DECLS
+#  endif
+#endif
+
+__BEGIN_DECLS
+
+gap_t *gap_new(void);
+int gap_dispose(gap_t **/*gap*/);
+uint64_t gap_exec(gap_t */*gap*/, const char */*action*/, const void */*vs*/, uint64_t /*n*/);
+char *gap_exec_mem(gap_t */*gap*/, const char */*action*/, uint64_t /*n*/, uint64_t */*size*/);
+int gap_search(gap_t */*gap*/, const char */*params*/, const char */*pat*/, uint64_t /*mc*/, void */*mv*/);
+
+__END_DECLS
+
+#endif

Reply via email to