summaryrefslogtreecommitdiff
path: root/src/libexecline
diff options
context:
space:
mode:
Diffstat (limited to 'src/libexecline')
-rwxr-xr-xsrc/libexecline/deps-lib/execline21
-rw-r--r--src/libexecline/el_execsequence.c41
-rw-r--r--src/libexecline/el_getstrict.c18
-rw-r--r--src/libexecline/el_obsolescent.c10
-rw-r--r--src/libexecline/el_popenv.c44
-rw-r--r--src/libexecline/el_pushenv.c49
-rw-r--r--src/libexecline/el_semicolon.c35
-rw-r--r--src/libexecline/el_spawn0.c15
-rw-r--r--src/libexecline/el_spawn1.c15
-rw-r--r--src/libexecline/el_substandrun.c13
-rw-r--r--src/libexecline/el_substandrun_str.c25
-rw-r--r--src/libexecline/el_substitute.c179
-rw-r--r--src/libexecline/el_transform.c84
-rw-r--r--src/libexecline/el_vardupl.c12
-rw-r--r--src/libexecline/exlp.c78
-rw-r--r--src/libexecline/exlsn_define.c50
-rw-r--r--src/libexecline/exlsn_elglob.c78
-rw-r--r--src/libexecline/exlsn_exlp.c27
-rw-r--r--src/libexecline/exlsn_free.c12
-rw-r--r--src/libexecline/exlsn_import.c76
-rw-r--r--src/libexecline/exlsn_main.c19
-rw-r--r--src/libexecline/exlsn_multidefine.c79
22 files changed, 980 insertions, 0 deletions
diff --git a/src/libexecline/deps-lib/execline b/src/libexecline/deps-lib/execline
new file mode 100755
index 0000000..69d483d
--- /dev/null
+++ b/src/libexecline/deps-lib/execline
@@ -0,0 +1,21 @@
+el_execsequence.o
+el_getstrict.o
+el_obsolescent.o
+el_popenv.o
+el_pushenv.o
+el_semicolon.o
+el_spawn0.o
+el_spawn1.o
+el_substandrun.o
+el_substandrun_str.o
+el_substitute.o
+el_transform.o
+el_vardupl.o
+exlsn_define.o
+exlsn_elglob.o
+exlsn_import.o
+exlsn_multidefine.o
+exlsn_exlp.o
+exlsn_main.o
+exlsn_free.o
+exlp.o
diff --git a/src/libexecline/el_execsequence.c b/src/libexecline/el_execsequence.c
new file mode 100644
index 0000000..6b825af
--- /dev/null
+++ b/src/libexecline/el_execsequence.c
@@ -0,0 +1,41 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#ifdef EXECLINE_OLD_VARNAMES
+#include <skalibs/bytestr.h>
+#endif
+#include <skalibs/djbunix.h>
+#include <skalibs/env.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/uint.h>
+#include <execline/execline.h>
+
+void el_execsequence (char const *const *argv1, char const *const *argv2, char const *const *envp)
+{
+ if (!argv2[0])
+ {
+ pathexec0_run(argv1, envp) ;
+ strerr_dieexec(111, argv1[0]) ;
+ }
+ else
+ {
+ int wstat ;
+ unsigned int j = 2 ;
+#ifdef EXECLINE_OLD_VARNAMES
+ char fmt[UINT_FMT * 2 + 15] = "?=" ;
+#else
+ char fmt[UINT_FMT + 1] = "?=" ;
+#endif
+ pid_t pid = el_spawn0(argv1[0], argv1, envp) ;
+ if (!pid) strerr_warnwu2sys("spawn ", argv1[0]) ;
+ if (wait_pid(pid, &wstat) < 0)
+ strerr_diefu2sys(111, "wait for ", argv1[0]) ;
+ j += uint_fmt(fmt + j, wait_status(wstat)) ; fmt[j++] = 0 ;
+#ifdef EXECLINE_OLD_VARNAMES
+ byte_copy(fmt + j, 13, "LASTEXITCODE=") ; j += 13 ;
+ j += uint_fmt(fmt + j, wait_status(wstat)) ; fmt[j++] = 0 ;
+#endif
+ pathexec_r(argv2, envp, env_len(envp), fmt, j) ;
+ }
+ strerr_dieexec(111, argv2[0]) ;
+}
diff --git a/src/libexecline/el_getstrict.c b/src/libexecline/el_getstrict.c
new file mode 100644
index 0000000..091845c
--- /dev/null
+++ b/src/libexecline/el_getstrict.c
@@ -0,0 +1,18 @@
+/* ISC license. */
+
+#include <skalibs/env.h>
+#include <skalibs/uint.h>
+#include <execline/execline.h>
+
+unsigned int el_getstrict (void)
+{
+ static unsigned int strict = 0 ;
+ static int first = 1 ;
+ if (first)
+ {
+ char const *x = env_get("EXECLINE_STRICT") ;
+ first = 0 ;
+ if (x) uint0_scan(x, &strict) ;
+ }
+ return strict ;
+}
diff --git a/src/libexecline/el_obsolescent.c b/src/libexecline/el_obsolescent.c
new file mode 100644
index 0000000..42b8a11
--- /dev/null
+++ b/src/libexecline/el_obsolescent.c
@@ -0,0 +1,10 @@
+/* ISC license. */
+
+#include <skalibs/strerr2.h>
+#include <execline/execline.h>
+
+void el_obsolescent (void)
+{
+ if (el_getstrict())
+ strerr_warnw3x("this command is marked as obsolescent. Please update your script to use the ", PROG, "x command instead.") ;
+}
diff --git a/src/libexecline/el_popenv.c b/src/libexecline/el_popenv.c
new file mode 100644
index 0000000..3726506
--- /dev/null
+++ b/src/libexecline/el_popenv.c
@@ -0,0 +1,44 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/uint.h>
+#include <execline/execline.h>
+
+int el_popenv (stralloc *sa, char const *const *envp, unsigned int envlen, char const *const *list, unsigned int listlen)
+{
+ unsigned int i = 0, salen = sa->len, count = 0 ;
+ for (; i < envlen ; i++)
+ {
+ unsigned int equal, colon, n ;
+ unsigned int j = 0 ;
+ for (; j < listlen ; j++) if (str_start(envp[i], list[j])) break ;
+ if (j == listlen) goto copyit ;
+ j = str_len(list[j]) ;
+ colon = j + str_chr(envp[i] + j, ':') ;
+ equal = j + str_chr(envp[i] + j, '=') ;
+ if (!envp[i][equal]) goto badenv ;
+ if (colon >= equal) { count++ ; continue ; }
+ if (colon + 1 + uint_scan(envp[i] + colon + 1, &n) != equal) goto copyit ;
+ if (!n) goto copyit ;
+ if (!stralloc_catb(sa, envp[i], colon)) goto err ;
+ if (n > 1)
+ {
+ char fmt[UINT_FMT+1] = ":" ;
+ n = 1 + uint_fmt(fmt+1, n-1) ;
+ if (!stralloc_catb(sa, fmt, n)) goto err ;
+ }
+ if (!stralloc_catb(sa, envp[i] + equal, str_len(envp[i] + equal) + 1)) goto err ;
+ continue ;
+copyit:
+ if (!stralloc_catb(sa, envp[i], str_len(envp[i]) + 1)) goto err ;
+ }
+ return (int)count ;
+
+badenv :
+ errno = EINVAL ;
+err:
+ sa->len = salen ;
+ return -1 ;
+}
diff --git a/src/libexecline/el_pushenv.c b/src/libexecline/el_pushenv.c
new file mode 100644
index 0000000..9b9608d
--- /dev/null
+++ b/src/libexecline/el_pushenv.c
@@ -0,0 +1,49 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/uint.h>
+#include <execline/execline.h>
+
+int el_pushenv (stralloc *sa, char const *const *envp, unsigned int envlen, char const *const *list, unsigned int listlen)
+{
+ unsigned int i = 0, salen = sa->len, count = 0 ;
+ for (; i < envlen ; i++)
+ {
+ unsigned int equal, colon ;
+ unsigned int j = 0 ;
+ for (; j < listlen ; j++) if (str_start(envp[i], list[j])) break ;
+ if (j == listlen) goto copyit ;
+ count++ ;
+ j = str_len(list[j]) ;
+ colon = j + str_chr(envp[i] + j, ':') ;
+ equal = j + str_chr(envp[i] + j, '=') ;
+ if (!envp[i][equal]) goto badenv ;
+ if (colon >= equal)
+ {
+ if (!stralloc_catb(sa, envp[i], equal)
+ || !stralloc_catb(sa, ":1", 2)) goto err ;
+ }
+ else
+ {
+ char fmt[UINT_FMT+1] = ":" ;
+ unsigned int n ;
+ if (colon + 1 + uint_scan(envp[i] + colon + 1, &n) != equal) goto copyit ;
+ n = 1 + uint_fmt(fmt+1, n+1) ;
+ if (!stralloc_catb(sa, envp[i], colon)) goto err ;
+ if (!stralloc_catb(sa, fmt, n)) goto err ;
+ }
+ if (!stralloc_catb(sa, envp[i] + equal, str_len(envp[i] + equal) + 1)) goto err ;
+ continue ;
+copyit:
+ if (!stralloc_catb(sa, envp[i], str_len(envp[i]) + 1)) goto err ;
+ }
+ return (int)count ;
+
+badenv :
+ errno = EINVAL ;
+err:
+ sa->len = salen ;
+ return -1 ;
+}
diff --git a/src/libexecline/el_semicolon.c b/src/libexecline/el_semicolon.c
new file mode 100644
index 0000000..dc99daf
--- /dev/null
+++ b/src/libexecline/el_semicolon.c
@@ -0,0 +1,35 @@
+/* ISC license. */
+
+#include <skalibs/env.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/uint.h>
+#include <execline/execline.h>
+
+int el_semicolon (char const **argv)
+{
+ static unsigned int nblock = 0 ;
+ register int argc1 = 0 ;
+ nblock++ ;
+ for (;; argc1++, argv++)
+ {
+ register char const *arg = *argv ;
+ if (!arg) return argc1 + 1 ;
+ if ((arg[0] == EXECLINE_BLOCK_END_CHAR) && (!EXECLINE_BLOCK_END_CHAR || !arg[1])) return argc1 ;
+ else if (arg[0] == EXECLINE_BLOCK_QUOTE_CHAR) ++*argv ;
+ else
+ {
+ unsigned int strict = el_getstrict() ;
+ if (strict)
+ {
+ char fmt1[UINT_FMT] ;
+ char fmt2[UINT_FMT] ;
+ fmt1[uint_fmt(fmt1, nblock)] = 0 ;
+ fmt2[uint_fmt(fmt2, (unsigned int)argc1)] = 0 ;
+ if (strict >= 2)
+ strerr_dief6x(100, "unquoted argument ", arg, " at block ", fmt1, " position ", fmt2) ;
+ else
+ strerr_warnw6x("unquoted argument ", arg, " at block ", fmt1, " position ", fmt2) ;
+ }
+ }
+ }
+}
diff --git a/src/libexecline/el_spawn0.c b/src/libexecline/el_spawn0.c
new file mode 100644
index 0000000..4b7f50f
--- /dev/null
+++ b/src/libexecline/el_spawn0.c
@@ -0,0 +1,15 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <skalibs/djbunix.h>
+#include <execline/execline.h>
+
+pid_t el_spawn0 (char const *prog, char const *const *argv, char const *const *envp)
+{
+ if (!argv[0])
+ {
+ static char const *const newargv[2] = { "/bin/true", 0 } ;
+ return child_spawn0(newargv[0], newargv, 0) ;
+ }
+ else return child_spawn0(prog, argv, envp) ;
+}
diff --git a/src/libexecline/el_spawn1.c b/src/libexecline/el_spawn1.c
new file mode 100644
index 0000000..4785b92
--- /dev/null
+++ b/src/libexecline/el_spawn1.c
@@ -0,0 +1,15 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <skalibs/djbunix.h>
+#include <execline/execline.h>
+
+pid_t el_spawn1 (char const *prog, char const *const *argv, char const *const *envp, int *fd, int w)
+{
+ if (!argv[0])
+ {
+ static char const *const newargv[2] = { "/bin/true", 0 } ;
+ return child_spawn1(newargv[0], newargv, 0, fd, w) ;
+ }
+ else return child_spawn1(prog, argv, envp, fd, w) ;
+}
diff --git a/src/libexecline/el_substandrun.c b/src/libexecline/el_substandrun.c
new file mode 100644
index 0000000..7dddbca
--- /dev/null
+++ b/src/libexecline/el_substandrun.c
@@ -0,0 +1,13 @@
+/* ISC license. */
+
+#include <skalibs/env.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/skamisc.h>
+#include "exlsn.h"
+
+void el_substandrun (int argc, char const *const *argv, char const *const *envp, exlsn_t *info)
+{
+ satmp.len = 0 ;
+ if (!env_string(&satmp, argv, (unsigned int)argc)) strerr_diefu1sys(111, "env_string") ;
+ el_substandrun_str(&satmp, 0, envp, info) ;
+}
diff --git a/src/libexecline/el_substandrun_str.c b/src/libexecline/el_substandrun_str.c
new file mode 100644
index 0000000..351ec9d
--- /dev/null
+++ b/src/libexecline/el_substandrun_str.c
@@ -0,0 +1,25 @@
+/* ISC license. */
+
+#include <skalibs/djbunix.h>
+#include <skalibs/env.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <execline/execline.h>
+#include "exlsn.h"
+
+void el_substandrun_str (stralloc *src, unsigned int srcbase, char const *const *envp, exlsn_t *info)
+{
+ stralloc dst = STRALLOC_ZERO ;
+ register int r = el_substitute(&dst, src->s + srcbase, src->len, info->vars.s, info->values.s, genalloc_s(elsubst_t, &info->data), genalloc_len(elsubst_t, &info->data)) ;
+ if (r < 0) strerr_diefu1sys(111, "el_substitute") ;
+ exlsn_free(info) ;
+ stralloc_free(src) ;
+ {
+ char const *v[r + 1] ;
+ if (!env_make(v, r, dst.s, dst.len)) strerr_diefu1sys(111, "env_make") ;
+ v[r] = 0 ;
+ pathexec0_run(v, envp) ;
+ }
+ strerr_dieexec(111, dst.s) ;
+}
diff --git a/src/libexecline/el_substitute.c b/src/libexecline/el_substitute.c
new file mode 100644
index 0000000..a94c16d
--- /dev/null
+++ b/src/libexecline/el_substitute.c
@@ -0,0 +1,179 @@
+/* ISC license. */
+
+#include <skalibs/bytestr.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <execline/execline.h>
+
+typedef struct elsubsu_s elsubsu_t, *elsubsu_t_ref ;
+struct elsubsu_s
+{
+ elsubst_t const *subst ;
+ unsigned int pos ;
+} ;
+
+typedef struct subsuinfo_s subsuinfo_t, *subsuinfo_t_ref ;
+struct subsuinfo_s
+{
+ stralloc dst ;
+ stralloc sa ;
+ genalloc list ; /* array of elsubsu_t */
+ char const *values ;
+} ;
+
+#define SUBSUINFO_ZERO { .dst = STRALLOC_ZERO, .sa = STRALLOC_ZERO, .list = GENALLOC_ZERO, .values = 0 }
+
+#define TEST 0x80
+#define MARK 0x40
+#define KEEPESC 0x20
+#define INCRESC 0x10
+
+#define STATE 0x07
+#define INWORD 0x00
+#define INDOLL 0x01
+#define INDBR 0x02
+#define INVAR 0x03
+#define INVARBR 0x04
+#define ACCEPT 0x05
+
+static int parseword (stralloc *sa, genalloc *list, char const *s, char const *vars, elsubst_t const *substs, unsigned int nsubst)
+{
+ static char const class[5] = "\0\\${}" ;
+ static unsigned char const table[6][5] =
+ {
+ { ACCEPT, ACCEPT, ACCEPT, ACCEPT | TEST, ACCEPT },
+ { INWORD | KEEPESC | INCRESC, INWORD | INCRESC, INWORD | INCRESC, INWORD | TEST | INCRESC, INWORD | INCRESC },
+ { INDOLL | KEEPESC, INDOLL, INDOLL, INDOLL | TEST, INDOLL },
+ { INWORD, INDBR | KEEPESC, INWORD, INWORD | TEST, INWORD },
+ { INWORD, INWORD, INWORD, INWORD | TEST, INWORD | TEST },
+ { INWORD, INVAR | MARK | KEEPESC, INVARBR | MARK | KEEPESC, INVAR | KEEPESC, INVARBR | KEEPESC }
+ } ;
+
+ unsigned int mark = 0, pos = 0, offset = 0, esc = 0, salen = sa->len, listlen = genalloc_len(elsubsu_t, list) ;
+ unsigned char state = INWORD ;
+
+ while (state != ACCEPT)
+ {
+ int nopush = 0 ;
+ unsigned char c = table[byte_chr(class, 5, s[pos])][state] ;
+
+ if (c & TEST)
+ {
+ unsigned int supp = (state == INVARBR) ;
+ unsigned int i = 0 ;
+ for (; i < nsubst ; i++)
+ {
+ if (!str_diffn(vars + substs[i].var, s + mark, pos - mark) && !vars[substs[i].var + pos - mark])
+ {
+ sa->len -= esc >> 1 ; offset += esc >> 1 ;
+ if (esc & 1)
+ {
+ byte_copy(sa->s + mark - offset - 2 - supp, pos - mark + 1 + supp, sa->s + mark - offset + (esc>>1) - 1 - supp) ;
+ sa->len-- ; offset++ ;
+ }
+ else
+ {
+ elsubsu_t cur ;
+ cur.subst = substs + i ;
+ cur.pos = mark - offset - 1 - supp ;
+ if (!genalloc_append(elsubsu_t, list, &cur)) goto err ;
+ offset += sa->len - cur.pos ;
+ sa->len = cur.pos ;
+ if (supp) nopush = 1 ;
+ }
+ break ;
+ }
+ }
+ }
+ if (nopush) offset++ ;
+ else if (!stralloc_catb(sa, s+pos, 1)) goto err ;
+ if (c & MARK) mark = pos ;
+ if (!(c & KEEPESC)) esc = 0 ;
+ if (c & INCRESC) esc++ ;
+ state = c & STATE ; pos++ ;
+ }
+ sa->len-- ;
+ return (int)pos ;
+
+err:
+ sa->len = salen ;
+ list->len = listlen ;
+ return -1 ;
+}
+
+static int substword (subsuinfo_t *info, unsigned int wordstart, unsigned int wordlen, unsigned int n, unsigned int offset)
+{
+ if (n < genalloc_len(elsubsu_t, &info->list))
+ {
+ elsubsu_t *list = genalloc_s(elsubsu_t, &info->list) ;
+ char const *p = info->values + list[n].subst->value ;
+ unsigned int l = list[n].pos + offset ;
+ unsigned int dstbase = info->dst.len ;
+ unsigned int sabase = info->sa.len ;
+ unsigned int i = 0 ;
+ int nc = 0 ;
+ if (!stralloc_readyplus(&info->sa, l)) return -1 ;
+ stralloc_catb(&info->sa, info->sa.s + wordstart, l) ;
+ for ( ; i < list[n].subst->n ; i++)
+ {
+ int r ;
+ unsigned int plen = str_len(p) ;
+ info->sa.len = sabase + l ;
+ if (!stralloc_readyplus(&info->sa, plen + wordlen - l)) goto err ;
+ stralloc_catb(&info->sa, p, plen) ;
+ stralloc_catb(&info->sa, info->sa.s + wordstart + l, wordlen - l) ;
+ r = substword(info, sabase, info->sa.len - sabase, n+1, offset + plen) ;
+ if (r < 0) goto err ;
+ nc += r ;
+ p += plen+1 ;
+ }
+ return nc ;
+ err:
+ info->sa.len = sabase ;
+ info->dst.len = dstbase ;
+ return -1 ;
+ }
+ else
+ {
+ if (!stralloc_readyplus(&info->dst, wordlen+1)) return -1 ;
+ stralloc_catb(&info->dst, info->sa.s + wordstart, wordlen) ;
+ stralloc_0(&info->dst) ;
+ return 1 ;
+ }
+}
+
+int el_substitute (stralloc *dst, char const *src, unsigned int len, char const *vars, char const *values, elsubst_t const *substs, unsigned int nsubst)
+{
+ subsuinfo_t info = SUBSUINFO_ZERO ;
+ unsigned int nc = 0 ;
+ unsigned int i = 0 ;
+ unsigned int dstbase = dst->len ;
+ int wasnull = !dst->s ;
+ info.dst = *dst ;
+ info.values = values ;
+
+ while (i < len)
+ {
+ int r ;
+ genalloc_setlen(elsubsu_t, &info.list, 0) ;
+ info.sa.len = 0 ;
+ r = parseword(&info.sa, &info.list, src + i, vars, substs, nsubst) ;
+ if (r < 0) goto err ;
+ i += r ;
+ r = substword(&info, 0, info.sa.len, 0, 0) ;
+ if (r < 0) goto err ;
+ nc += r ;
+ }
+ genalloc_free(elsubsu_t, &info.list) ;
+ stralloc_free(&info.sa) ;
+ if (!wasnull) stralloc_free(dst) ;
+ *dst = info.dst ;
+ return (int)nc ;
+
+err :
+ genalloc_free(elsubsu_t, &info.list) ;
+ stralloc_free(&info.sa) ;
+ if (wasnull) stralloc_free(&info.dst) ; else info.dst.len = dstbase ;
+ *dst = info.dst ;
+ return -1 ;
+}
diff --git a/src/libexecline/el_transform.c b/src/libexecline/el_transform.c
new file mode 100644
index 0000000..ac84d1d
--- /dev/null
+++ b/src/libexecline/el_transform.c
@@ -0,0 +1,84 @@
+/* ISC license. */
+
+#include <skalibs/bytestr.h>
+#include <skalibs/netstring.h>
+#include <skalibs/skamisc.h>
+#include <skalibs/stralloc.h>
+#include <execline/execline.h>
+
+static void el_crunch (stralloc *sa, unsigned int base, char const *delim)
+{
+ register unsigned int i = base, j = base ;
+ register int crunching = 0 ;
+ for (; i < sa->len ; i++)
+ {
+ if (!crunching) sa->s[j++] = sa->s[i] ;
+ if (delim[str_chr(delim, sa->s[i])]) crunching = 1 ;
+ else if (crunching)
+ {
+ i-- ;
+ crunching = 0 ;
+ }
+ }
+ sa->len = j ;
+}
+
+static int el_split (stralloc *sa, unsigned int base, eltransforminfo_t const *si, int chomped)
+{
+ unsigned int n = 0 ;
+ register unsigned int i = base ;
+ for (; i < sa->len ; i++)
+ if (si->delim[str_chr(si->delim, sa->s[i])])
+ {
+ sa->s[i] = 0 ;
+ n++ ;
+ base = i+1 ;
+ }
+
+ if (sa->len && sa->s[sa->len - 1])
+ {
+ if (si->chomp && !chomped) sa->len = base ;
+ else if (!stralloc_0(sa)) return -1 ;
+ else n++ ;
+ }
+ return n ;
+}
+
+static int el_splitnetstring (stralloc *sa, unsigned int base)
+{
+ unsigned int tmpbase = satmp.len ;
+ unsigned int n = 0, i = base ;
+ while (i < sa->len)
+ {
+ register int r = netstring_decode(&satmp, sa->s + i, sa->len - i) ;
+ if (r < 0) goto err ;
+ if (!stralloc_0(&satmp)) goto err ;
+ i += r ; n++ ;
+ }
+ sa->len = base ;
+ if (!stralloc_catb(sa, satmp.s + tmpbase, satmp.len - tmpbase))
+ {
+ sa->len = i ;
+ goto err ;
+ }
+ satmp.len = tmpbase ;
+ return n ;
+
+err:
+ satmp.len = tmpbase ;
+ return -1 ;
+}
+
+int el_transform (stralloc *sa, unsigned int i, eltransforminfo_t const *si)
+{
+ int chomped = 0 ;
+ if (si->crunch && *si->delim) el_crunch(sa, i, si->delim) ;
+ if (si->chomp && (sa->len > i)
+ && si->delim[str_chr(si->delim, sa->s[sa->len-1])])
+ {
+ sa->len-- ;
+ chomped = 1 ;
+ }
+ return si->split ? *si->delim ? el_split(sa, i, si, chomped) : el_splitnetstring(sa, i) :
+ stralloc_0(sa) ? 1 : -1 ;
+}
diff --git a/src/libexecline/el_vardupl.c b/src/libexecline/el_vardupl.c
new file mode 100644
index 0000000..7583669
--- /dev/null
+++ b/src/libexecline/el_vardupl.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <skalibs/bytestr.h>
+#include <execline/execline.h>
+
+int el_vardupl (char const *key, char const *s, unsigned int len)
+{
+ register unsigned int i = 0 ;
+ for (; i < len ; i += str_len(s + i) + 1)
+ if (!str_diff(key, s + i)) return 1 ;
+ return 0 ;
+}
diff --git a/src/libexecline/exlp.c b/src/libexecline/exlp.c
new file mode 100644
index 0000000..060eb68
--- /dev/null
+++ b/src/libexecline/exlp.c
@@ -0,0 +1,78 @@
+/* ISC license. */
+
+#include <skalibs/bytestr.h>
+#include <skalibs/env.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/uint.h>
+#include <execline/execline.h>
+#include "exlsn.h"
+
+int exlp (unsigned int nmin, char const *const *envp, exlsn_t *info)
+{
+ unsigned int varbase = info->vars.len ;
+ unsigned int valbase = info->values.len ;
+ unsigned int datbase = genalloc_len(elsubst_t, &info->data) ;
+ unsigned int i = 0 ;
+ char const *x = env_get2(envp, "#") ;
+ elsubst_t blah ;
+ unsigned int n, ntot, poszero ;
+ if (!x) return -2 ;
+ if (!uint0_scan(x, &n)) return -2 ;
+ if (el_vardupl("#", info->vars.s, info->vars.len)) return -2 ;
+ if (el_vardupl("@", info->vars.s, info->vars.len)) return -2 ;
+ {
+ register unsigned int strict = el_getstrict() ;
+ if (strict && (n < nmin))
+ {
+ char fmta[UINT_FMT] ;
+ char fmtn[UINT_FMT] ;
+ fmta[uint_fmt(fmta, n)] = 0 ;
+ fmtn[uint_fmt(fmtn, nmin)] = 0 ;
+ if (strict > 1)
+ strerr_dief4x(100, "too few arguments: expected at least ", fmtn, " but got ", fmta) ;
+ else
+ strerr_warnw4x("too few arguments: expected at least ", fmtn, " but got ", fmta) ;
+ }
+ }
+ blah.var = varbase ;
+ blah.value = info->values.len ;
+ blah.n = 1 ;
+ if (!stralloc_catb(&info->vars, "#\0@", 4)
+ || !stralloc_catb(&info->values, x, str_len(x) + 1)
+ || !genalloc_append(elsubst_t, &info->data, &blah)) goto err ;
+ ntot = n > nmin ? n : nmin ;
+ poszero = info->values.len ;
+ for (; i <= ntot ; i++)
+ {
+ char fmt[UINT_FMT] ;
+ unsigned int l = uint_fmt(fmt, i) ;
+ fmt[l] = 0 ;
+ if (el_vardupl(fmt, info->vars.s, info->vars.len)) goto err2 ;
+ x = (i <= n) ? env_get2(envp, fmt) : "" ;
+ if (!x) goto err2 ;
+ blah.var = info->vars.len ;
+ blah.value = info->values.len ;
+ blah.n = 1 ;
+ if (!stralloc_catb(&info->vars, fmt, l+1)
+ || !stralloc_catb(&info->values, x, str_len(x) + 1)
+ || !genalloc_append(elsubst_t, &info->data, &blah)) goto err ;
+ }
+ blah.var = varbase + 2 ;
+ blah.value = poszero + str_len(info->values.s + poszero) + 1 ;
+ blah.n = n ;
+ if (!genalloc_append(elsubst_t, &info->data, &blah)) goto err ;
+ return n ;
+
+ err:
+ info->vars.len = varbase ;
+ info->values.len = valbase ;
+ genalloc_setlen(elsubst_t, &info->data, datbase) ;
+ return -1 ;
+ err2:
+ info->vars.len = varbase ;
+ info->values.len = valbase ;
+ genalloc_setlen(elsubst_t, &info->data, datbase) ;
+ return -2 ;
+}
diff --git a/src/libexecline/exlsn_define.c b/src/libexecline/exlsn_define.c
new file mode 100644
index 0000000..3abe516
--- /dev/null
+++ b/src/libexecline/exlsn_define.c
@@ -0,0 +1,50 @@
+/* ISC license. */
+
+#include <skalibs/bytestr.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <execline/execline.h>
+#include "exlsn.h"
+
+int exlsn_define (int argc, char const **argv, char const *const *envp, exlsn_t *info)
+{
+ eltransforminfo_t si = ELTRANSFORMINFO_ZERO ;
+ subgetopt_t localopt = SUBGETOPT_ZERO ;
+ elsubst_t blah ;
+ blah.var = info->vars.len ;
+ blah.value = info->values.len ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "nsCcd:", &localopt) ;
+ if (opt < 0) break ;
+ switch (opt)
+ {
+ case 'n' : si.chomp = 1 ; break ;
+ case 's' : si.split = 1 ; break ;
+ case 'C' : si.crunch = 1 ; break ;
+ case 'c' : si.crunch = 0 ; break ;
+ case 'd' : si.delim = localopt.arg ; break ;
+ default : return -3 ;
+ }
+ }
+ argc -= localopt.ind ; argv += localopt.ind ;
+
+ if (argc < 2) return -3 ;
+ if (!*argv[0] || el_vardupl(argv[0], info->vars.s, info->vars.len)) return -2 ;
+ if (!stralloc_catb(&info->vars, argv[0], str_len(argv[0]) + 1)) return -1 ;
+ if (!stralloc_cats(&info->values, argv[1])) goto err ;
+ {
+ register int r = el_transform(&info->values, blah.value, &si) ;
+ if (r < 0) goto err ;
+ blah.n = r ;
+ }
+ if (!genalloc_append(elsubst_t, &info->data, &blah)) goto err ;
+ (void)envp ;
+ return localopt.ind + 2 ;
+
+ err:
+ info->vars.len = blah.var ;
+ info->values.len = blah.value ;
+ return -1 ;
+}
diff --git a/src/libexecline/exlsn_elglob.c b/src/libexecline/exlsn_elglob.c
new file mode 100644
index 0000000..67e939b
--- /dev/null
+++ b/src/libexecline/exlsn_elglob.c
@@ -0,0 +1,78 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <glob.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <execline/execline.h>
+#include "exlsn.h"
+
+static int elgloberrfunc (char const *s, int e)
+{
+ errno = e ;
+ strerr_warnw2sys("while globbing, error reading ", s) ;
+ return 0 ;
+}
+
+int exlsn_elglob (int argc, char const **argv, char const *const *envp, exlsn_t *info)
+{
+ glob_t pglob ;
+ subgetopt_t localopt = SUBGETOPT_ZERO ;
+ elsubst_t blah ;
+ int flags = GLOB_NOSORT | GLOB_NOCHECK ;
+ unsigned int i = 0 ;
+ int verbose = 0 ;
+ blah.var = info->vars.len ;
+ blah.value = info->values.len ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "vwsme0", &localopt) ;
+ if (opt < 0) break ;
+ switch (opt)
+ {
+ case 'v' : verbose = 1 ; break ;
+ case 'w' : flags |= GLOB_ERR ; break ;
+ case 's' : flags &= ~GLOB_NOSORT ; break ;
+ case 'm' : flags |= GLOB_MARK ; break ;
+ case 'e' : flags |= GLOB_NOESCAPE ; break ;
+ case '0' : flags &= ~GLOB_NOCHECK ; break ;
+ default : return -3 ;
+ }
+ }
+ argc -= localopt.ind ; argv += localopt.ind ;
+
+ if (argc < 2) return -3 ;
+ if (!*argv[0] || el_vardupl(argv[0], info->vars.s, info->vars.len)) return -2 ;
+ if (!stralloc_catb(&info->vars, argv[0], str_len(argv[0]) + 1)) return -1 ;
+
+ pglob.gl_offs = 0 ;
+ switch (glob(argv[1], flags, verbose ? &elgloberrfunc : 0, &pglob))
+ {
+ case 0 : break ;
+ case GLOB_NOMATCH:
+ {
+ pglob.gl_pathc = 0 ;
+ pglob.gl_pathv = 0 ;
+ break ;
+ }
+ default: goto err ;
+ }
+ for ( ; i < (unsigned int)pglob.gl_pathc ; i++)
+ if (!stralloc_catb(&info->values, pglob.gl_pathv[i], str_len(pglob.gl_pathv[i]) + 1))
+ goto globerr ;
+ blah.n = pglob.gl_pathc ;
+ globfree(&pglob) ;
+ if (!genalloc_append(elsubst_t, &info->data, &blah)) goto err ;
+ (void)envp ;
+ return localopt.ind + 2 ;
+
+ globerr:
+ globfree(&pglob) ;
+ err:
+ info->vars.len = blah.var ;
+ info->values.len = blah.value ;
+ return -1 ;
+}
diff --git a/src/libexecline/exlsn_exlp.c b/src/libexecline/exlsn_exlp.c
new file mode 100644
index 0000000..4ca513e
--- /dev/null
+++ b/src/libexecline/exlsn_exlp.c
@@ -0,0 +1,27 @@
+/* ISC license. */
+
+#include <skalibs/sgetopt.h>
+#include <skalibs/uint.h>
+#include "exlsn.h"
+
+int exlsn_exlp (int argc, char const **argv, char const *const *envp, exlsn_t *info)
+{
+ subgetopt_t localopt = SUBGETOPT_ZERO ;
+ unsigned int nmin = 0 ;
+ int n ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "P:", &localopt) ;
+ if (opt < 0) break ;
+ switch (opt)
+ {
+ case 'P' : if (uint0_scan(localopt.arg, &nmin)) break ;
+ default : return -3 ;
+ }
+ }
+ argc -= localopt.ind ; argv += localopt.ind ;
+ if (!argc) return -3 ;
+ n = exlp(nmin, envp, info) ;
+ if (n < 0) return n ;
+ return localopt.ind ;
+}
diff --git a/src/libexecline/exlsn_free.c b/src/libexecline/exlsn_free.c
new file mode 100644
index 0000000..4d9dde3
--- /dev/null
+++ b/src/libexecline/exlsn_free.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <skalibs/stralloc.h>
+#include "exlsn.h"
+
+void exlsn_free (exlsn_t *info)
+{
+ stralloc_free(&info->vars) ;
+ stralloc_free(&info->values) ;
+ stralloc_free(&info->data) ;
+}
+
diff --git a/src/libexecline/exlsn_import.c b/src/libexecline/exlsn_import.c
new file mode 100644
index 0000000..1574027
--- /dev/null
+++ b/src/libexecline/exlsn_import.c
@@ -0,0 +1,76 @@
+/* ISC license. */
+
+#include <skalibs/bytestr.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/env.h>
+#include <execline/execline.h>
+#include "exlsn.h"
+
+static int exlsn_import_as (int argc, char const **argv, char const *const *envp, exlsn_t *info, unsigned int as)
+{
+ eltransforminfo_t si = ELTRANSFORMINFO_ZERO ;
+ subgetopt_t localopt = SUBGETOPT_ZERO ;
+ elsubst_t blah ;
+ char const *defaultval = 0 ;
+ char const *x ;
+ int insist = 0 ;
+ blah.var = info->vars.len ;
+ blah.value = info->values.len ;
+
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "iD:nsCcd:", &localopt) ;
+ if (opt < 0) break ;
+ switch (opt)
+ {
+ case 'i' : insist = 1 ; break ;
+ case 'D' : defaultval = localopt.arg ; break ;
+ case 'n' : si.chomp = 1 ; break ;
+ case 's' : si.split = 1 ; break ;
+ case 'C' : si.crunch = 1 ; break ;
+ case 'c' : si.crunch = 0 ; break ;
+ case 'd' : si.delim = localopt.arg ; break ;
+ default : return -3 ;
+ }
+ }
+ argc -= localopt.ind ; argv += localopt.ind ;
+
+ if ((unsigned int)argc < 1+as) return -3 ;
+ if (!*argv[0] || el_vardupl(argv[0], info->vars.s, info->vars.len)) return -2 ;
+ if (!stralloc_catb(&info->vars, argv[0], str_len(argv[0]) + 1)) return -1 ;
+ x = env_get2(envp, argv[as]) ;
+ if (!x)
+ {
+ if (insist) strerr_dienotset(100, argv[as]) ;
+ x = defaultval ;
+ }
+ if (!x) blah.n = 0 ;
+ else
+ {
+ register int r ;
+ if (!stralloc_cats(&info->values, x)) goto err ;
+ r = el_transform(&info->values, blah.value, &si) ;
+ if (r < 0) goto err ;
+ blah.n = r ;
+ }
+ if (!genalloc_append(elsubst_t, &info->data, &blah)) goto err ;
+ return localopt.ind + 1 + as ;
+
+ err:
+ info->vars.len = blah.var ;
+ info->values.len = blah.value ;
+ return -1 ;
+}
+
+int exlsn_import (int argc, char const **argv, char const *const *envp, exlsn_t *info)
+{
+ return exlsn_import_as(argc, argv, envp, info, 0) ;
+}
+
+int exlsn_importas (int argc, char const **argv, char const *const *envp, exlsn_t *info)
+{
+ return exlsn_import_as(argc, argv, envp, info, 1) ;
+}
diff --git a/src/libexecline/exlsn_main.c b/src/libexecline/exlsn_main.c
new file mode 100644
index 0000000..e50fc2d
--- /dev/null
+++ b/src/libexecline/exlsn_main.c
@@ -0,0 +1,19 @@
+/* ISC license. */
+
+#include <skalibs/strerr2.h>
+#include <execline/execline.h>
+#include "exlsn.h"
+
+void exlsn_main (int argc, char const **argv, char const *const *envp, exlsnfunc_t *func, char const *usage)
+{
+ exlsn_t info = EXLSN_ZERO ;
+ int r = (*func)(argc, argv, envp, &info) ;
+ if (r < 0) switch (r)
+ {
+ case -3 : strerr_dieusage(100, usage) ;
+ case -2 : strerr_dief1x(111, "bad substitution key") ;
+ case -1 : strerr_diefu1sys(111, "complete exlsn function") ;
+ default : strerr_diefu2x(111, "complete exlsn function", ": unknown error") ;
+ }
+ el_substandrun(argc-r, argv+r, envp, &info) ;
+}
diff --git a/src/libexecline/exlsn_multidefine.c b/src/libexecline/exlsn_multidefine.c
new file mode 100644
index 0000000..e64cbfd
--- /dev/null
+++ b/src/libexecline/exlsn_multidefine.c
@@ -0,0 +1,79 @@
+/* ISC license. */
+
+#include <skalibs/sgetopt.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <execline/execline.h>
+#include "exlsn.h"
+
+int exlsn_multidefine (int argc, char const **argv, char const *const *envp, exlsn_t *info)
+{
+ eltransforminfo_t si = ELTRANSFORMINFO_ZERO ;
+ subgetopt_t localopt = SUBGETOPT_ZERO ;
+ unsigned int varbase = info->vars.len ;
+ unsigned int valbase = info->values.len ;
+ unsigned int pos = valbase ;
+ unsigned int i = 0 ;
+ unsigned int max ;
+ char const *x ;
+ int argc1 ;
+ int zeroword = 0, likeread = 0 ;
+ si.split = 1 ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "0rnCcd:", &localopt) ;
+ if (opt < 0) break ;
+ switch (opt)
+ {
+ case '0' : zeroword = 1 ; break ;
+ case 'r' : likeread = 1 ; break ;
+ case 'n' : si.chomp = 1 ; break ;
+ case 'C' : si.crunch = 1 ; break ;
+ case 'c' : si.crunch = 0 ; break ;
+ case 'd' : si.delim = localopt.arg ; break ;
+ default : return -3 ;
+ }
+ }
+ argc -= localopt.ind ; argv += localopt.ind ;
+
+ if (argc < 2) return -3 ;
+ x = argv[0] ;
+ argv++ ; argc-- ;
+ argc1 = el_semicolon(argv) ;
+ if (argc1 >= argc) return -3 ;
+ if (!stralloc_cats(&info->values, x)) return -1 ;
+ {
+ register int r = el_transform(&info->values, valbase, &si) ;
+ if (r < 0) goto err ;
+ max = r ;
+ }
+ if (!stralloc_0(&info->values)) goto err ;
+ for (; i < (unsigned int)argc1 ; i++)
+ {
+ if (*argv[i])
+ {
+ elsubst_t blah ;
+ blah.var = info->vars.len ;
+ if (el_vardupl(argv[i], info->vars.s, info->vars.len)) goto err2 ;
+ if (!stralloc_catb(&info->vars, argv[i], str_len(argv[i]) + 1)) goto err ;
+ blah.value = i < max ? pos : info->values.len - 1 ;
+ blah.n = (i < max) || !zeroword ;
+ if (!genalloc_append(elsubst_t, &info->data, &blah)) goto err ;
+ }
+ if (i < max) pos += str_len(info->values.s + pos) + 1 ;
+ }
+ if ((i < max) && likeread) genalloc_s(elsubst_t, &info->data)[i-1].n = max - i + 1 ;
+
+ (void)envp ;
+ return localopt.ind + argc1 + 2 ;
+
+ err:
+ info->vars.len = varbase ;
+ info->values.len = valbase ;
+ return -1 ;
+ err2:
+ info->vars.len = varbase ;
+ info->values.len = valbase ;
+ return -2 ;
+}