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 <[email protected]>
+ * 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(¶ms, 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, ¶ms, 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 <[email protected]>
+ * 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