summary refs log tree commit diff
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2015-03-26 12:47:19 +0000
committerLaurent Bercot <ska-skaware@skarnet.org>2015-03-26 12:47:19 +0000
commit9cf89b4437df6a7741532dd73fed770cd8eef9f2 (patch)
treeabdeb249d992b1ba90d44b838318d0d09c3369a2
parentcfe736ed5615d6e00f4ce6704591782fe790a752 (diff)
downloadexecline-9cf89b4437df6a7741532dd73fed770cd8eef9f2.tar.gz
execline-9cf89b4437df6a7741532dd73fed770cd8eef9f2.tar.xz
execline-9cf89b4437df6a7741532dd73fed770cd8eef9f2.zip
- added el_parse() and friends
 - refactored execlineb to use them
 - version: rc for 2.1.1.1
-rw-r--r--INSTALL2
-rw-r--r--doc/index.html4
-rw-r--r--doc/upgrade.html8
-rw-r--r--package/deps.mak7
-rw-r--r--package/info2
-rw-r--r--src/execline/execlineb.c177
-rw-r--r--src/include/execline/execline.h11
-rw-r--r--src/libexecline/deps-lib/execline3
-rw-r--r--src/libexecline/el_parse.c87
-rw-r--r--src/libexecline/el_parse_from_buffer.c18
-rw-r--r--src/libexecline/el_parse_from_string.c15
11 files changed, 170 insertions, 164 deletions
diff --git a/INSTALL b/INSTALL
index 9a8f893..c2d9b7a 100644
--- a/INSTALL
+++ b/INSTALL
@@ -6,7 +6,7 @@ Build Instructions
 
   - A POSIX-compliant C development environment
   - GNU make version 4.0 or later
-  - skalibs version 2.3.1.0 or later: http://skarnet.org/software/skalibs/
+  - skalibs version 2.3.2.0 or later: http://skarnet.org/software/skalibs/
 
  This software will run on any operating system that implements
 POSIX.1-2008, available at:
diff --git a/doc/index.html b/doc/index.html
index bc2e020..ede99d1 100644
--- a/doc/index.html
+++ b/doc/index.html
@@ -51,7 +51,7 @@ shell's syntax, and has no security issues.
  <li> GNU make, version 4.0 or later. Please be aware that execline will
 not build with an earlier version. </li>
  <li> <a href="http://skarnet.org/software/skalibs/">skalibs</a> version
-2.3.1.0 or later. It's a build-time requirement. It's also a run-time
+2.3.2.0 or later. It's a build-time requirement. It's also a run-time
 requirement if you link against the shared version of the skalibs
 library. </li>
 </ul>
@@ -66,7 +66,7 @@ library. </li>
 <h3> Download </h3>
 
 <ul>
- <li> The current released version of execline is <a href="execline-2.1.1.0.tar.gz">2.1.1.0</a>. </li>
+ <li> The current released version of execline is <a href="execline-2.1.1.1.tar.gz">2.1.1.1</a>. </li>
  <li> Alternatively, you can checkout a copy of the execline git repository:
 <pre> git clone git://git.skarnet.org/execline </pre> </li>
 </ul>
diff --git a/doc/upgrade.html b/doc/upgrade.html
index 1e6ab8c..32516d7 100644
--- a/doc/upgrade.html
+++ b/doc/upgrade.html
@@ -17,6 +17,14 @@
 
 <h1> What has changed in execline </h1>
 
+<h2> in 2.1.1.1 </h2>
+
+<ul>
+ <li> skalibs dependency bumped to 2.3.2.0 </li>
+ <li> execlineb parser made into a library function, for easier
+inclusion in other programs </li>
+</ul>
+
 <h2> in 2.1.1.0 </h2>
 
 <ul>
diff --git a/package/deps.mak b/package/deps.mak
index 5c77ee5..6c926ae 100644
--- a/package/deps.mak
+++ b/package/deps.mak
@@ -47,6 +47,9 @@ src/execline/unexport.o src/execline/unexport.lo: src/execline/unexport.c
 src/execline/wait.o src/execline/wait.lo: src/execline/wait.c src/include/execline/execline.h
 src/libexecline/el_execsequence.o src/libexecline/el_execsequence.lo: src/libexecline/el_execsequence.c src/include/execline/execline.h
 src/libexecline/el_getstrict.o src/libexecline/el_getstrict.lo: src/libexecline/el_getstrict.c src/include/execline/execline.h
+src/libexecline/el_parse.o src/libexecline/el_parse.lo: src/libexecline/el_parse.c src/include/execline/execline.h
+src/libexecline/el_parse_from_buffer.o src/libexecline/el_parse_from_buffer.lo: src/libexecline/el_parse_from_buffer.c src/include/execline/execline.h
+src/libexecline/el_parse_from_string.o src/libexecline/el_parse_from_string.lo: src/libexecline/el_parse_from_string.c src/include/execline/execline.h
 src/libexecline/el_popenv.o src/libexecline/el_popenv.lo: src/libexecline/el_popenv.c src/include/execline/execline.h
 src/libexecline/el_pushenv.o src/libexecline/el_pushenv.lo: src/libexecline/el_pushenv.c src/include/execline/execline.h
 src/libexecline/el_semicolon.o src/libexecline/el_semicolon.lo: src/libexecline/el_semicolon.c src/include/execline/execline.h
@@ -152,5 +155,5 @@ unexport: private EXTRA_LIBS :=
 unexport: src/execline/unexport.o -lskarnet
 wait: private EXTRA_LIBS :=
 wait: src/execline/wait.o ${LIBEXECLINE} -lskarnet
-libexecline.a:  src/libexecline/el_execsequence.o src/libexecline/el_getstrict.o src/libexecline/el_popenv.o src/libexecline/el_pushenv.o src/libexecline/el_semicolon.o src/libexecline/el_spawn0.o src/libexecline/el_spawn1.o src/libexecline/el_substandrun.o src/libexecline/el_substandrun_str.o src/libexecline/el_substitute.o src/libexecline/el_transform.o src/libexecline/el_vardupl.o src/libexecline/exlsn_define.o src/libexecline/exlsn_elglob.o src/libexecline/exlsn_import.o src/libexecline/exlsn_multidefine.o src/libexecline/exlsn_exlp.o src/libexecline/exlsn_main.o src/libexecline/exlsn_free.o src/libexecline/exlp.o
-libexecline.so:  src/libexecline/el_execsequence.lo src/libexecline/el_getstrict.lo src/libexecline/el_popenv.lo src/libexecline/el_pushenv.lo src/libexecline/el_semicolon.lo src/libexecline/el_spawn0.lo src/libexecline/el_spawn1.lo src/libexecline/el_substandrun.lo src/libexecline/el_substandrun_str.lo src/libexecline/el_substitute.lo src/libexecline/el_transform.lo src/libexecline/el_vardupl.lo src/libexecline/exlsn_define.lo src/libexecline/exlsn_elglob.lo src/libexecline/exlsn_import.lo src/libexecline/exlsn_multidefine.lo src/libexecline/exlsn_exlp.lo src/libexecline/exlsn_main.lo src/libexecline/exlsn_free.lo src/libexecline/exlp.lo
+libexecline.a:  src/libexecline/el_execsequence.o src/libexecline/el_getstrict.o src/libexecline/el_parse.o src/libexecline/el_parse_from_buffer.o src/libexecline/el_parse_from_string.o src/libexecline/el_popenv.o src/libexecline/el_pushenv.o src/libexecline/el_semicolon.o src/libexecline/el_spawn0.o src/libexecline/el_spawn1.o src/libexecline/el_substandrun.o src/libexecline/el_substandrun_str.o src/libexecline/el_substitute.o src/libexecline/el_transform.o src/libexecline/el_vardupl.o src/libexecline/exlsn_define.o src/libexecline/exlsn_elglob.o src/libexecline/exlsn_import.o src/libexecline/exlsn_multidefine.o src/libexecline/exlsn_exlp.o src/libexecline/exlsn_main.o src/libexecline/exlsn_free.o src/libexecline/exlp.o
+libexecline.so:  src/libexecline/el_execsequence.lo src/libexecline/el_getstrict.lo src/libexecline/el_parse.lo src/libexecline/el_parse_from_buffer.lo src/libexecline/el_parse_from_string.lo src/libexecline/el_popenv.lo src/libexecline/el_pushenv.lo src/libexecline/el_semicolon.lo src/libexecline/el_spawn0.lo src/libexecline/el_spawn1.lo src/libexecline/el_substandrun.lo src/libexecline/el_substandrun_str.lo src/libexecline/el_substitute.lo src/libexecline/el_transform.lo src/libexecline/el_vardupl.lo src/libexecline/exlsn_define.lo src/libexecline/exlsn_elglob.lo src/libexecline/exlsn_import.lo src/libexecline/exlsn_multidefine.lo src/libexecline/exlsn_exlp.lo src/libexecline/exlsn_main.lo src/libexecline/exlsn_free.lo src/libexecline/exlp.lo
diff --git a/package/info b/package/info
index f57cc18..4e34898 100644
--- a/package/info
+++ b/package/info
@@ -1,4 +1,4 @@
 package=execline
-version=2.1.1.0
+version=2.1.1.1
 category=admin
 package_macro_name=EXECLINE
diff --git a/src/execline/execlineb.c b/src/execline/execlineb.c
index 1b9e7ad..bfe1d7e 100644
--- a/src/execline/execlineb.c
+++ b/src/execline/execlineb.c
@@ -1,6 +1,6 @@
 /* ISC license. */
 
-#include <skalibs/uint16.h>
+#include <errno.h>
 #include <skalibs/uint.h>
 #include <skalibs/allreadwrite.h>
 #include <skalibs/sgetopt.h>
@@ -9,6 +9,7 @@
 #include <skalibs/strerr2.h>
 #include <skalibs/stralloc.h>
 #include <skalibs/genalloc.h>
+#include <skalibs/env.h>
 #include <skalibs/djbunix.h>
 #include <skalibs/skamisc.h>
 #include <execline/execline.h>
@@ -16,152 +17,6 @@
 
 #define USAGE "execlineb [ -p | -P | -S nmin ] [ -q | -w | -W ] [ -c commandline ] script args"
 
-typedef unsigned char chargen_t (void) ;
-
-/* Action (strongest 11 bits) */
-
-#define PUSH 0x8000
-#define PUSH0 0x4000
-#define PUSHSPECIAL 0x2000
-#define SETBASE 0x1000
-#define MARK 0x0800
-#define CALC 0x0400
-#define QUOTE 0x0200
-#define INCB 0x0100
-#define DECB 0x0080
-
-
-/* State (weakest 5 bits) */
-
-#define MAIN 0x00
-#define INWORD 0x01
-#define INWORDESC 0x02
-#define INSTR 0x03
-#define INSTRESC 0x04
-#define INREM 0x05
-#define OCT0 0x06
-#define OCT1 0x07
-#define OCT2 0x08
-#define DEC1 0x09
-#define DEC2 0x0a
-#define HEX0 0x0b
-#define HEX1 0x0c
-#define ENDCALC 0x0d
-#define OPENB 0x0e
-#define CLOSEB 0x0f
-#define ERROR 0x10
-#define ACCEPT 0x11
-
-static buffer b ;
-
-static void initbuffer (char const *s)
-{
-  static char buf[BUFFER_INSIZE] ;
-  int fd = open_readb(s) ;
-  if (fd < 0) strerr_diefu3sys(111, "open ", s, " for reading") ;
-  if (coe(fd) < 0) strerr_diefu2sys(111, "coe ", s) ;
-  buffer_init(&b, &buffer_read, fd, buf, BUFFER_INSIZE) ;
-}
-
-static unsigned char nextinbuffer ()
-{
-  char c ;
-  switch (buffer_get(&b, &c, 1))
-  {
-    case -1: strerr_diefu1sys(111, "read script") ;
-    case 0 : return 0 ;
-  }
-  return (unsigned char)c ;
-}
-
-static unsigned char const *string = 0 ;
-
-static unsigned char nextinstring ()
-{
-  static unsigned int pos = 0 ;
-  return string[pos++] ;
-}
-
-static int lex (stralloc *sa, chargen_t *next)
-{
-  static unsigned char const class[256] = "`aaaaaaaaadaaaaaaaaaaaaaaaaaaaaaafcbffffffffffffjhhhhhhhiifffffffmmmmmmfffffffffffffffffffffeffffggmmmgfffffffkfffkfkfkflffnfoffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" ;
-  static uint16 const table[16][16] =
-  {
-    { 0x0011, 0x4011, 0x0010, 0x0010, 0x0010, 0x0011, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x4091 },
-    { 0x0000, 0x4000, 0x8001, 0x8003, 0x8003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x0100, 0x4080 },
-    { 0x0005, 0x8001, 0x8001, 0x8003, 0x8003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x8001, 0x8001 },
-    { 0x0203, 0x0003, 0x8001, 0x0001, 0x8003, 0x0005, 0x0010, 0x0401, 0x0401, 0x0401, 0x0401, 0x0010, 0x0401, 0x0401, 0x0003, 0x0003 },
-    { 0x0000, 0x4000, 0x8001, 0x8003, 0x0003, 0x0000, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x0100, 0x4080 },
-    { 0x0202, 0x0002, 0x8001, 0x0004, 0x8003, 0x0005, 0x0010, 0x0404, 0x0404, 0x0404, 0x0404, 0x0010, 0x0404, 0x0404, 0x0002, 0x0002 },
-    { 0x8201, 0x8001, 0x8001, 0x8003, 0x8003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x8001, 0x8001 },
-    { 0x8201, 0x8001, 0x8001, 0x8003, 0x2003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x880c, 0x800d, 0x8403, 0x8001, 0x8001 },
-    { 0x8201, 0x8001, 0x8001, 0x8003, 0x9809, 0x0005, 0x8807, 0x8008, 0x800d, 0x800a, 0x800d, 0x880c, 0x800d, 0x8403, 0x8001, 0x8001 },
-    { 0x8201, 0x8001, 0x8001, 0x8003, 0x9809, 0x0005, 0x0010, 0x8403, 0x8403, 0x800a, 0x800d, 0x880c, 0x800d, 0x8403, 0x8001, 0x8001 },
-    { 0x8201, 0x8001, 0x8001, 0x8003, 0x1006, 0x0005, 0x8807, 0x8008, 0x800d, 0x800a, 0x800d, 0x880c, 0x800d, 0x8403, 0x8001, 0x8001 },
-    { 0x8201, 0x8001, 0x8001, 0x8003, 0x2003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x8001, 0x8001 },
-    { 0x8201, 0x8001, 0x8001, 0x8003, 0x8003, 0x0005, 0x100b, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x8001, 0x8001 },
-    { 0x8201, 0x8001, 0x8001, 0x8003, 0x8003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x880c, 0x800d, 0x8403, 0x8001, 0x8001 },
-    { 0x820e, 0x8001, 0x8001, 0x8003, 0x8003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x8001, 0x8001 },
-    { 0x820f, 0x8001, 0x8001, 0x8003, 0x8003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x8001, 0x8001 }
-  } ;
-
-  unsigned int mark = 0 ;
-  unsigned int n = 0 ;
-  unsigned char state = MAIN, base = 10 ;
-  unsigned int blevel = 0 ;
-
-  while (state < ERROR)
-  {
-    unsigned char cur = (*next)() ;
-    register uint16 c = table[class[cur]-'`'][state] ;
-    state = c & 0x1F ;
-
- /* Actions. The order is important ! */
-
-    if (c & CALC)
-    {
-      unsigned int z ;
-      if (!stralloc_0(sa)) return -1 ;
-      sa->len = mark ;
-      uint_scan_base(sa->s + sa->len, &z, base) ;
-      sa->s[sa->len++] = (unsigned char)z ;
-    }
-    if (c & MARK) mark = sa->len ;
-    if (c & QUOTE)
-    {
-      char tilde = EXECLINE_BLOCK_QUOTE_CHAR ;
-      register unsigned int i = blevel ;
-      if (!stralloc_readyplus(sa, i<<1)) return -1 ;
-      while (i--) stralloc_catb(sa, &tilde, 1) ;
-    }
-    if (c & INCB) sa->len -= ++blevel ;
-    if (c & DECB)
-    {
-      if (!blevel--) return -4 ;
-      sa->s[--sa->len-1] = EXECLINE_BLOCK_END_CHAR ;
-      if (!EXECLINE_BLOCK_END_CHAR) sa->len-- ;
-    }
-    if (c & PUSH) if (!stralloc_catb(sa, (char *)&cur, 1)) return -1 ;
-    if (c & PUSHSPECIAL)
-    {
-      char x = 7 + byte_chr("abtnvfr", 7, cur) ;
-      if (!stralloc_catb(sa, &x, 1)) return -1 ;
-    }
-    if (c & PUSH0) if (n++, !stralloc_0(sa)) return -1 ;
-    if (c & SETBASE)
-      switch (cur)
-      {
-        case 'x' : base = 16 ; break ;
-        case '0' : base = 8 ; break ;
-        default : base = 10 ;
-      }
-  }
-  if (state == ERROR) return -2 ;
-  if (blevel) return -3 ;
-  return n ;
-}
-
-
 static int myexlp (stralloc *sa, char const *const *argv, unsigned int argc, unsigned int nmin, char const *dollar0)
 {
   exlsn_t info = EXLSN_ZERO ;
@@ -205,16 +60,15 @@ static int myexlp (stralloc *sa, char const *const *argv, unsigned int argc, uns
   return -1 ;
 }
 
-
 int main (int argc, char const *const *argv, char const *const *envp)
 {
-  chargen_t *next ;
   stralloc sa = STRALLOC_ZERO ;
   stralloc modif = STRALLOC_ZERO ;
   int nc ;
   int flagstrict = -1 ;
   unsigned int nmin = 0 ;
-  char const *dollar0 = argv[0] ;
+  char const *stringarg = 0 ;
+  char const *dollar0 = *argv ;
   unsigned int flagpushenv = 2 ;
   PROG = "execlineb" ;
   {
@@ -230,7 +84,7 @@ int main (int argc, char const *const *argv, char const *const *envp)
         case 'q' : flagstrict = 0 ; break ;
         case 'w' : flagstrict = 1 ; break ;
         case 'W' : flagstrict = 2 ; break ;
-        case 'c' : string = (unsigned char *)l.arg ; break ;
+        case 'c' : stringarg = l.arg ; break ;
         case 'S' :
         {
           if (!uint0_scan(l.arg, &nmin)) strerr_dieusage(100, USAGE) ;
@@ -242,17 +96,24 @@ int main (int argc, char const *const *argv, char const *const *envp)
     }
     argc -= l.ind ; argv += l.ind ;
   }
-  if (string) next = &nextinstring ;
+  if (stringarg) nc = el_parse_from_string(&sa, stringarg) ;
   else
   {
-    if (!argv[0]) strerr_dieusage(100, USAGE) ;
-    initbuffer(argv[0]) ;
-    dollar0 = argv[0] ;
-    argv++ ; argc-- ;
-    next = &nextinbuffer ;
+    char buf[BUFFER_INSIZE] ;
+    buffer b ;
+    int fd ;
+    int e ;
+    if (!argc--) strerr_dieusage(100, USAGE) ;
+    dollar0 = *argv++ ;
+    fd = open_readb(dollar0) ;
+    if (fd < 0) strerr_diefu3sys(111, "open ", dollar0, " for reading") ;
+    buffer_init(&b, &fd_readsv, fd, buf, BUFFER_INSIZE) ;
+    nc = el_parse_from_buffer(&sa, &b) ;
+    e = errno ;
+    fd_close(fd) ;
+    errno = e ;
   }
 
-  nc = lex(&sa, next) ;
   switch (nc)
   {
     case -4: strerr_dief2x(100, "unmatched ", "}") ;
diff --git a/src/include/execline/execline.h b/src/include/execline/execline.h
index 1f1ec68..7691cf1 100644
--- a/src/include/execline/execline.h
+++ b/src/include/execline/execline.h
@@ -5,12 +5,23 @@
 
 #include <sys/types.h>
 #include <skalibs/gccattributes.h>
+#include <skalibs/buffer.h>
 #include <skalibs/stralloc.h>
 
 #define EXECLINE_BLOCK_QUOTE_CHAR ' '
 #define EXECLINE_BLOCK_END_CHAR '\0'
 
 
+/* Parsing */
+
+typedef int el_chargen_func_t (unsigned char *, void *) ;
+typedef el_chargen_func_t *el_chargen_func_t_ref ;
+
+extern int el_parse (stralloc *, el_chargen_func_t_ref, void *) ;
+extern int el_parse_from_string (stralloc *, char const *) ;
+extern int el_parse_from_buffer (stralloc *, buffer *) ;
+
+
 /* Basics */
 
 extern int el_vardupl (char const *, char const *, unsigned int) gccattr_pure ;
diff --git a/src/libexecline/deps-lib/execline b/src/libexecline/deps-lib/execline
index f3e4e49..da9d62e 100644
--- a/src/libexecline/deps-lib/execline
+++ b/src/libexecline/deps-lib/execline
@@ -1,5 +1,8 @@
 el_execsequence.o
 el_getstrict.o
+el_parse.o
+el_parse_from_buffer.o
+el_parse_from_string.o
 el_popenv.o
 el_pushenv.o
 el_semicolon.o
diff --git a/src/libexecline/el_parse.c b/src/libexecline/el_parse.c
new file mode 100644
index 0000000..1544f51
--- /dev/null
+++ b/src/libexecline/el_parse.c
@@ -0,0 +1,87 @@
+/* ISC license. */
+
+#include <skalibs/uint16.h>
+#include <skalibs/uint.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/djbunix.h>
+#include <execline/execline.h>
+
+int el_parse (stralloc *sa, el_chargen_func_t_ref next, void *source)
+{
+  static unsigned char const class[256] = "`aaaaaaaaadaaaaaaaaaaaaaaaaaaaaaafcbffffffffffffjhhhhhhhiifffffffmmmmmmfffffffffffffffffffffeffffggmmmgfffffffkfffkfkfkflffnfoffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" ;
+  static uint16 const table[16][16] =
+  {
+    { 0x0011, 0x4011, 0x0010, 0x0010, 0x0010, 0x0011, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x4091 },
+    { 0x0000, 0x4000, 0x8001, 0x8003, 0x8003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x0100, 0x4080 },
+    { 0x0005, 0x8001, 0x8001, 0x8003, 0x8003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x8001, 0x8001 },
+    { 0x0203, 0x0003, 0x8001, 0x0001, 0x8003, 0x0005, 0x0010, 0x0401, 0x0401, 0x0401, 0x0401, 0x0010, 0x0401, 0x0401, 0x0003, 0x0003 },
+    { 0x0000, 0x4000, 0x8001, 0x8003, 0x0003, 0x0000, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x0100, 0x4080 },
+    { 0x0202, 0x0002, 0x8001, 0x0004, 0x8003, 0x0005, 0x0010, 0x0404, 0x0404, 0x0404, 0x0404, 0x0010, 0x0404, 0x0404, 0x0002, 0x0002 },
+    { 0x8201, 0x8001, 0x8001, 0x8003, 0x8003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x8001, 0x8001 },
+    { 0x8201, 0x8001, 0x8001, 0x8003, 0x2003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x880c, 0x800d, 0x8403, 0x8001, 0x8001 },
+    { 0x8201, 0x8001, 0x8001, 0x8003, 0x9809, 0x0005, 0x8807, 0x8008, 0x800d, 0x800a, 0x800d, 0x880c, 0x800d, 0x8403, 0x8001, 0x8001 },
+    { 0x8201, 0x8001, 0x8001, 0x8003, 0x9809, 0x0005, 0x0010, 0x8403, 0x8403, 0x800a, 0x800d, 0x880c, 0x800d, 0x8403, 0x8001, 0x8001 },
+    { 0x8201, 0x8001, 0x8001, 0x8003, 0x1006, 0x0005, 0x8807, 0x8008, 0x800d, 0x800a, 0x800d, 0x880c, 0x800d, 0x8403, 0x8001, 0x8001 },
+    { 0x8201, 0x8001, 0x8001, 0x8003, 0x2003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x8001, 0x8001 },
+    { 0x8201, 0x8001, 0x8001, 0x8003, 0x8003, 0x0005, 0x100b, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x8001, 0x8001 },
+    { 0x8201, 0x8001, 0x8001, 0x8003, 0x8003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x880c, 0x800d, 0x8403, 0x8001, 0x8001 },
+    { 0x820e, 0x8001, 0x8001, 0x8003, 0x8003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x8001, 0x8001 },
+    { 0x820f, 0x8001, 0x8001, 0x8003, 0x8003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x8001, 0x8001 }
+  } ;
+
+  unsigned int mark = 0 ;
+  unsigned int n = 0 ;
+  unsigned int blevel = 0 ;
+  unsigned char state = 0, base = 10 ;
+
+  while (state < 0x10)
+  {
+    uint16 c ;
+    unsigned char cur ;
+    if (!(*next)(&cur, source)) return -1 ;
+    c = table[class[cur]-'`'][state] ;
+    state = c & 0x1F ;
+
+    if (c & 0x0400)
+    {
+      unsigned int z ;
+      if (!stralloc_0(sa)) return -1 ;
+      sa->len = mark ;
+      uint_scan_base(sa->s + sa->len, &z, base) ;
+      sa->s[sa->len++] = (unsigned char)z ;
+    }
+    if (c & 0x0800) mark = sa->len ;
+    if (c & 0x0200)
+    {
+      char tilde = EXECLINE_BLOCK_QUOTE_CHAR ;
+      register unsigned int i = blevel ;
+      if (!stralloc_readyplus(sa, i<<1)) return -1 ;
+      while (i--) stralloc_catb(sa, &tilde, 1) ;
+    }
+    if (c & 0x0100) sa->len -= ++blevel ;
+    if (c & 0x0080)
+    {
+      if (!blevel--) return -4 ;
+      sa->s[--sa->len-1] = EXECLINE_BLOCK_END_CHAR ;
+      if (!EXECLINE_BLOCK_END_CHAR) sa->len-- ;
+    }
+    if (c & 0x8000) if (!stralloc_catb(sa, (char *)&cur, 1)) return -1 ;
+    if (c & 0x2000)
+    {
+      char x = 7 + byte_chr("abtnvfr", 7, cur) ;
+      if (!stralloc_catb(sa, &x, 1)) return -1 ;
+    }
+    if (c & 0x4000) if (n++, !stralloc_0(sa)) return -1 ;
+    if (c & 0x1000)
+      switch (cur)
+      {
+        case 'x' : base = 16 ; break ;
+        case '0' : base = 8 ; break ;
+        default : base = 10 ;
+      }
+  }
+  if (state == 0x10) return -2 ;
+  if (blevel) return -3 ;
+  return n ;
+}
diff --git a/src/libexecline/el_parse_from_buffer.c b/src/libexecline/el_parse_from_buffer.c
new file mode 100644
index 0000000..802fe40
--- /dev/null
+++ b/src/libexecline/el_parse_from_buffer.c
@@ -0,0 +1,18 @@
+/* ISC license. */
+
+#include <skalibs/buffer.h>
+#include <skalibs/stralloc.h>
+#include <execline/execline.h>
+
+static int next (unsigned char *c, void *p)
+{
+  register int r = buffer_get((buffer *)p, (char *)c, 1) ;
+  if (r < 0) return 0 ;
+  if (!r) *c = 0 ;
+  return 1 ;
+}
+
+int el_parse_from_buffer (stralloc *sa, buffer *b)
+{
+  return el_parse(sa, &next, b) ;
+}
diff --git a/src/libexecline/el_parse_from_string.c b/src/libexecline/el_parse_from_string.c
new file mode 100644
index 0000000..04c48a3
--- /dev/null
+++ b/src/libexecline/el_parse_from_string.c
@@ -0,0 +1,15 @@
+/* ISC license. */
+
+#include <skalibs/stralloc.h>
+#include <execline/execline.h>
+
+static int next (unsigned char *c, void *p)
+{
+  *c = *(*(char const **)p)++ ;
+  return 1 ;
+}
+
+int el_parse_from_string (stralloc *sa, char const *s)
+{
+  return el_parse(sa, &next, &s) ;
+}