From f316a2ed52195135a35e32d7096e876357c48c69 Mon Sep 17 00:00:00 2001 From: Laurent Bercot Date: Thu, 18 Sep 2014 20:03:23 +0000 Subject: initial commit: rc for execline-2.0.0.0 --- .gitignore | 5 + AUTHORS | 16 ++ CHANGES | 266 +++++++++++++++++++++++ COPYING | 13 ++ INSTALL | 135 ++++++++++++ Makefile | 121 +++++++++++ README | 28 +++ README.macosx | 4 + README.solaris | 12 + configure | 385 +++++++++++++++++++++++++++++++++ doc/background.html | 57 +++++ doc/backtick.html | 66 ++++++ doc/cd.html | 45 ++++ doc/componentsb.txt | 41 ++++ doc/define.html | 42 ++++ doc/dieshdiedie.html | 278 ++++++++++++++++++++++++ doc/dollarat.html | 86 ++++++++ doc/el_pushenv.html | 173 +++++++++++++++ doc/el_semicolon.html | 124 +++++++++++ doc/el_substitute.html | 309 ++++++++++++++++++++++++++ doc/el_transform.html | 204 +++++++++++++++++ doc/elgetopt.html | 60 +++++ doc/elgetpositionals.html | 94 ++++++++ doc/elglob.html | 68 ++++++ doc/emptyenv.html | 57 +++++ doc/exec.html | 50 +++++ doc/execline-shell.html | 53 +++++ doc/execline-startup.html | 59 +++++ doc/execlineb.html | 246 +++++++++++++++++++++ doc/exit.html | 44 ++++ doc/export.html | 43 ++++ doc/fdblock.html | 55 +++++ doc/fdclose.html | 44 ++++ doc/fdmove.html | 55 +++++ doc/fdreserve.html | 92 ++++++++ doc/forbacktickx.html | 77 +++++++ doc/foreground.html | 57 +++++ doc/forx.html | 66 ++++++ doc/getpid.html | 44 ++++ doc/grammar.html | 160 ++++++++++++++ doc/heredoc.html | 56 +++++ doc/homeof.html | 37 ++++ doc/if.html | 68 ++++++ doc/ifelse.html | 59 +++++ doc/ifte.html | 67 ++++++ doc/ifthenelse.html | 59 +++++ doc/import.html | 37 ++++ doc/importas.html | 57 +++++ doc/index.html | 213 ++++++++++++++++++ doc/loopwhilex.html | 60 +++++ doc/multidefine.html | 69 ++++++ doc/multisubstitute.html | 121 +++++++++++ doc/pipeline.html | 67 ++++++ doc/piperw.html | 38 ++++ doc/quine-dam.txt | 110 ++++++++++ doc/quine-jriou.txt | 28 +++ doc/quine-prj-2.txt | 15 ++ doc/quine-prj-3.txt | 13 ++ doc/quine-prj.txt | 13 ++ doc/redirfd.html | 100 +++++++++ doc/runblock.html | 75 +++++++ doc/shift.html | 68 ++++++ doc/tryexec.html | 67 ++++++ doc/umask.html | 44 ++++ doc/unexport.html | 46 ++++ doc/upgrade.html | 35 +++ doc/wait.html | 53 +++++ package/deps-build | 1 + package/deps.mak | 110 ++++++++++ package/info | 4 + package/modes | 41 ++++ package/targets.mak | 51 +++++ patch-for-solaris | 17 ++ src/execline/background.c | 76 +++++++ src/execline/backtick.c | 84 +++++++ src/execline/cd.c | 17 ++ src/execline/define.c | 12 + src/execline/deps-exe/background | 2 + src/execline/deps-exe/backtick | 2 + src/execline/deps-exe/cd | 1 + src/execline/deps-exe/define | 2 + src/execline/deps-exe/dollarat | 1 + src/execline/deps-exe/elgetopt | 2 + src/execline/deps-exe/elgetpositionals | 2 + src/execline/deps-exe/elglob | 2 + src/execline/deps-exe/emptyenv | 2 + src/execline/deps-exe/exec | 1 + src/execline/deps-exe/execlineb | 2 + src/execline/deps-exe/exit | 1 + src/execline/deps-exe/export | 1 + src/execline/deps-exe/fdblock | 1 + src/execline/deps-exe/fdclose | 1 + src/execline/deps-exe/fdmove | 1 + src/execline/deps-exe/fdreserve | 1 + src/execline/deps-exe/forbacktickx | 2 + src/execline/deps-exe/foreground | 2 + src/execline/deps-exe/forx | 2 + src/execline/deps-exe/getpid | 1 + src/execline/deps-exe/heredoc | 1 + src/execline/deps-exe/homeof | 1 + src/execline/deps-exe/if | 2 + src/execline/deps-exe/ifelse | 2 + src/execline/deps-exe/ifte | 2 + src/execline/deps-exe/ifthenelse | 2 + src/execline/deps-exe/import | 2 + src/execline/deps-exe/importas | 2 + src/execline/deps-exe/loopwhilex | 2 + src/execline/deps-exe/multidefine | 2 + src/execline/deps-exe/multisubstitute | 2 + src/execline/deps-exe/pipeline | 2 + src/execline/deps-exe/piperw | 1 + src/execline/deps-exe/redirfd | 1 + src/execline/deps-exe/runblock | 2 + src/execline/deps-exe/shift | 2 + src/execline/deps-exe/tryexec | 2 + src/execline/deps-exe/umask | 1 + src/execline/deps-exe/unexport | 1 + src/execline/deps-exe/wait | 2 + src/execline/dollarat.c | 63 ++++++ src/execline/elgetopt.c | 69 ++++++ src/execline/elgetpositionals.c | 12 + src/execline/elglob.c | 12 + src/execline/emptyenv.c | 92 ++++++++ src/execline/exec.c | 48 ++++ src/execline/execlineb.c | 327 ++++++++++++++++++++++++++++ src/execline/exit.c | 15 ++ src/execline/export.c | 27 +++ src/execline/fdblock.c | 34 +++ src/execline/fdclose.c | 17 ++ src/execline/fdmove.c | 35 +++ src/execline/fdreserve.c | 64 ++++++ src/execline/forbacktickx.c | 145 +++++++++++++ src/execline/foreground.c | 14 ++ src/execline/forx.c | 91 ++++++++ src/execline/getpid.c | 29 +++ src/execline/heredoc.c | 59 +++++ src/execline/homeof.c | 28 +++ src/execline/if.c | 54 +++++ src/execline/ifelse.c | 54 +++++ src/execline/ifte.c | 59 +++++ src/execline/ifthenelse.c | 77 +++++++ src/execline/import.c | 12 + src/execline/importas.c | 12 + src/execline/loopwhilex.c | 62 ++++++ src/execline/multidefine.c | 12 + src/execline/multisubstitute.c | 64 ++++++ src/execline/pipeline.c | 85 ++++++++ src/execline/piperw.c | 29 +++ src/execline/redirfd.c | 69 ++++++ src/execline/runblock.c | 149 +++++++++++++ src/execline/shift.c | 121 +++++++++++ src/execline/tryexec.c | 61 ++++++ src/execline/umask.c | 20 ++ src/execline/unexport.c | 20 ++ src/execline/wait.c | 62 ++++++ src/include-local/exlsn.h | 36 +++ src/include/execline/execline.h | 67 ++++++ src/libexecline/deps-lib/execline | 21 ++ src/libexecline/el_execsequence.c | 41 ++++ src/libexecline/el_getstrict.c | 18 ++ src/libexecline/el_obsolescent.c | 10 + src/libexecline/el_popenv.c | 44 ++++ src/libexecline/el_pushenv.c | 49 +++++ src/libexecline/el_semicolon.c | 35 +++ src/libexecline/el_spawn0.c | 15 ++ src/libexecline/el_spawn1.c | 15 ++ src/libexecline/el_substandrun.c | 13 ++ src/libexecline/el_substandrun_str.c | 25 +++ src/libexecline/el_substitute.c | 179 +++++++++++++++ src/libexecline/el_transform.c | 84 +++++++ src/libexecline/el_vardupl.c | 12 + src/libexecline/exlp.c | 78 +++++++ src/libexecline/exlsn_define.c | 50 +++++ src/libexecline/exlsn_elglob.c | 78 +++++++ src/libexecline/exlsn_exlp.c | 27 +++ src/libexecline/exlsn_free.c | 12 + src/libexecline/exlsn_import.c | 76 +++++++ src/libexecline/exlsn_main.c | 19 ++ src/libexecline/exlsn_multidefine.c | 79 +++++++ tools/gen-deps.sh | 79 +++++++ tools/install.sh | 64 ++++++ 181 files changed, 9417 insertions(+) create mode 100644 .gitignore create mode 100644 AUTHORS create mode 100644 CHANGES create mode 100644 COPYING create mode 100644 INSTALL create mode 100644 Makefile create mode 100644 README create mode 100644 README.macosx create mode 100644 README.solaris create mode 100755 configure create mode 100644 doc/background.html create mode 100644 doc/backtick.html create mode 100644 doc/cd.html create mode 100644 doc/componentsb.txt create mode 100644 doc/define.html create mode 100644 doc/dieshdiedie.html create mode 100644 doc/dollarat.html create mode 100644 doc/el_pushenv.html create mode 100644 doc/el_semicolon.html create mode 100644 doc/el_substitute.html create mode 100644 doc/el_transform.html create mode 100644 doc/elgetopt.html create mode 100644 doc/elgetpositionals.html create mode 100644 doc/elglob.html create mode 100644 doc/emptyenv.html create mode 100644 doc/exec.html create mode 100644 doc/execline-shell.html create mode 100644 doc/execline-startup.html create mode 100644 doc/execlineb.html create mode 100644 doc/exit.html create mode 100644 doc/export.html create mode 100644 doc/fdblock.html create mode 100644 doc/fdclose.html create mode 100644 doc/fdmove.html create mode 100644 doc/fdreserve.html create mode 100644 doc/forbacktickx.html create mode 100644 doc/foreground.html create mode 100644 doc/forx.html create mode 100644 doc/getpid.html create mode 100644 doc/grammar.html create mode 100644 doc/heredoc.html create mode 100644 doc/homeof.html create mode 100644 doc/if.html create mode 100644 doc/ifelse.html create mode 100644 doc/ifte.html create mode 100644 doc/ifthenelse.html create mode 100644 doc/import.html create mode 100644 doc/importas.html create mode 100644 doc/index.html create mode 100644 doc/loopwhilex.html create mode 100644 doc/multidefine.html create mode 100644 doc/multisubstitute.html create mode 100644 doc/pipeline.html create mode 100644 doc/piperw.html create mode 100644 doc/quine-dam.txt create mode 100644 doc/quine-jriou.txt create mode 100644 doc/quine-prj-2.txt create mode 100644 doc/quine-prj-3.txt create mode 100644 doc/quine-prj.txt create mode 100644 doc/redirfd.html create mode 100644 doc/runblock.html create mode 100644 doc/shift.html create mode 100644 doc/tryexec.html create mode 100644 doc/umask.html create mode 100644 doc/unexport.html create mode 100644 doc/upgrade.html create mode 100644 doc/wait.html create mode 100644 package/deps-build create mode 100644 package/deps.mak create mode 100644 package/info create mode 100644 package/modes create mode 100644 package/targets.mak create mode 100755 patch-for-solaris create mode 100644 src/execline/background.c create mode 100644 src/execline/backtick.c create mode 100644 src/execline/cd.c create mode 100644 src/execline/define.c create mode 100644 src/execline/deps-exe/background create mode 100644 src/execline/deps-exe/backtick create mode 100644 src/execline/deps-exe/cd create mode 100644 src/execline/deps-exe/define create mode 100644 src/execline/deps-exe/dollarat create mode 100644 src/execline/deps-exe/elgetopt create mode 100644 src/execline/deps-exe/elgetpositionals create mode 100644 src/execline/deps-exe/elglob create mode 100644 src/execline/deps-exe/emptyenv create mode 100644 src/execline/deps-exe/exec create mode 100644 src/execline/deps-exe/execlineb create mode 100644 src/execline/deps-exe/exit create mode 100644 src/execline/deps-exe/export create mode 100644 src/execline/deps-exe/fdblock create mode 100644 src/execline/deps-exe/fdclose create mode 100644 src/execline/deps-exe/fdmove create mode 100644 src/execline/deps-exe/fdreserve create mode 100644 src/execline/deps-exe/forbacktickx create mode 100644 src/execline/deps-exe/foreground create mode 100644 src/execline/deps-exe/forx create mode 100644 src/execline/deps-exe/getpid create mode 100644 src/execline/deps-exe/heredoc create mode 100644 src/execline/deps-exe/homeof create mode 100644 src/execline/deps-exe/if create mode 100644 src/execline/deps-exe/ifelse create mode 100644 src/execline/deps-exe/ifte create mode 100644 src/execline/deps-exe/ifthenelse create mode 100644 src/execline/deps-exe/import create mode 100644 src/execline/deps-exe/importas create mode 100644 src/execline/deps-exe/loopwhilex create mode 100644 src/execline/deps-exe/multidefine create mode 100644 src/execline/deps-exe/multisubstitute create mode 100644 src/execline/deps-exe/pipeline create mode 100644 src/execline/deps-exe/piperw create mode 100644 src/execline/deps-exe/redirfd create mode 100644 src/execline/deps-exe/runblock create mode 100644 src/execline/deps-exe/shift create mode 100644 src/execline/deps-exe/tryexec create mode 100644 src/execline/deps-exe/umask create mode 100644 src/execline/deps-exe/unexport create mode 100644 src/execline/deps-exe/wait create mode 100644 src/execline/dollarat.c create mode 100644 src/execline/elgetopt.c create mode 100644 src/execline/elgetpositionals.c create mode 100644 src/execline/elglob.c create mode 100644 src/execline/emptyenv.c create mode 100644 src/execline/exec.c create mode 100644 src/execline/execlineb.c create mode 100644 src/execline/exit.c create mode 100644 src/execline/export.c create mode 100644 src/execline/fdblock.c create mode 100644 src/execline/fdclose.c create mode 100644 src/execline/fdmove.c create mode 100644 src/execline/fdreserve.c create mode 100644 src/execline/forbacktickx.c create mode 100644 src/execline/foreground.c create mode 100644 src/execline/forx.c create mode 100644 src/execline/getpid.c create mode 100644 src/execline/heredoc.c create mode 100644 src/execline/homeof.c create mode 100644 src/execline/if.c create mode 100644 src/execline/ifelse.c create mode 100644 src/execline/ifte.c create mode 100644 src/execline/ifthenelse.c create mode 100644 src/execline/import.c create mode 100644 src/execline/importas.c create mode 100644 src/execline/loopwhilex.c create mode 100644 src/execline/multidefine.c create mode 100644 src/execline/multisubstitute.c create mode 100644 src/execline/pipeline.c create mode 100644 src/execline/piperw.c create mode 100644 src/execline/redirfd.c create mode 100644 src/execline/runblock.c create mode 100644 src/execline/shift.c create mode 100644 src/execline/tryexec.c create mode 100644 src/execline/umask.c create mode 100644 src/execline/unexport.c create mode 100644 src/execline/wait.c create mode 100644 src/include-local/exlsn.h create mode 100644 src/include/execline/execline.h create mode 100755 src/libexecline/deps-lib/execline create mode 100644 src/libexecline/el_execsequence.c create mode 100644 src/libexecline/el_getstrict.c create mode 100644 src/libexecline/el_obsolescent.c create mode 100644 src/libexecline/el_popenv.c create mode 100644 src/libexecline/el_pushenv.c create mode 100644 src/libexecline/el_semicolon.c create mode 100644 src/libexecline/el_spawn0.c create mode 100644 src/libexecline/el_spawn1.c create mode 100644 src/libexecline/el_substandrun.c create mode 100644 src/libexecline/el_substandrun_str.c create mode 100644 src/libexecline/el_substitute.c create mode 100644 src/libexecline/el_transform.c create mode 100644 src/libexecline/el_vardupl.c create mode 100644 src/libexecline/exlp.c create mode 100644 src/libexecline/exlsn_define.c create mode 100644 src/libexecline/exlsn_elglob.c create mode 100644 src/libexecline/exlsn_exlp.c create mode 100644 src/libexecline/exlsn_free.c create mode 100644 src/libexecline/exlsn_import.c create mode 100644 src/libexecline/exlsn_main.c create mode 100644 src/libexecline/exlsn_multidefine.c create mode 100755 tools/gen-deps.sh create mode 100755 tools/install.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5c6415e --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.o +*.a +*.lo +*.so +*.so.* diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..24c57e1 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,16 @@ +Main author: + Laurent Bercot + +Contributors: + Jean Marot + Paul Jarc + +Thanks to: + Dan J. Bernstein + David Madore + Nicolas George + Joël Riou + Matthew R. Dempsky + Lasse Kliemann + Vallo Kallaste + Jorge Almeida diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000..a8f16b5 --- /dev/null +++ b/CHANGES @@ -0,0 +1,266 @@ +20020610 + packaging: corrected admin/ sticky bit +20020615 + bug: in execline.c, GETVAR couldn't read $1. + Fix: swap MARK and PUSH. + version: 0.11 +20020812 + bug: in execline.c, no actual shiftval support for $# + Fix: obvious. + bug: in wait.c, sizeof(unsigned int *) instead of sizeof(unsigned int) + Fix: obvious. + ui: fdclose doesn't die anymore on invalid fd + ui: fixed error messages in import, export, for, forbacktick + ui: removed unexported commands (moved to skaembutils) + internal: updated libstddjb + internal: fixed inclusion (for __errno_location) + packaging: fixed package/compat, split package/compile + doc: fixed if.html + version: 0.12 +20020830 + compat: package did not build on Solaris. + Fix: suppress ln -f, add ||true sentinels + internal: added to src/libstddjb/lock_*.c + internal: optimized emptyenv + packaging: revamped package/compile + version: 0.13 +20021001 + deps: added btdep to skalibs, removed included libstddjb + code: added loopwhile +20021002 + bug: fixed getopt handling in wait.c + internal: misc code cleanups + version: 0.14 +20021108 + internal: removed warnings + deps: moved to skalibs-0.14 + internal: sgetopt unneeded - switched to subgetopt. + internal: pre-alloc'ed execline lex buffer to avoid reallocs + internal: simplified ifthenelse tests +20021112 + code: added pipe +20021114 + code: added fdget +20021124 + doc: fixed import/importas interface + doc: fixed forbacktick's -n option warning +20030105 + deps: moved to skalibs-0.17 + code: added execlineb, documented it +20030106 + code: moved pipe to piperw, fdget to fdreserve + version: 0.15 +20030111 + code: added emptyenv -c, shift, elgetopt, dollarat +20030114 + deps: moved to skalibs-0.19 + code: rewrote execline(b) parser, new semantics +20030218 + code: wrote substalloc, el_substitute (that was the hard part) + internal: rewrote el_replace(s) to use el_substitute + ui: changed el_argv_make prototype + packaging: fixed package/upgrade +20030219 + ui: changed el_split prototype + code: wrote el_splitnetstring, rewrote dollarat + code: rewrote for, forbacktick + ui: discarded el_dollar + code: wrote elgetpositionals +20030220 + code: wrote multisubstitute + ui: rewrote execline-startup and execline-shell, moved them to .../etc +20030310 + doc: wrote all new doc +20030315 + code: added elglob, updated multisubstitute +20030317 + portability: FreeBSD-4.6 and glob() problem + workaround: used shglob + deps: moved to skalibs-0.21 +20030320 + version: 1.00 +20030322 + internal: separated libexls1.a + code: removed el_replace* + code: rewrote multisubstitute et al. + version: 1.01 +20030330 + deps: switched to skalibs-0.22 (fixed incompatible gen_alloc) +20030407 + internal: used gccattributes + doc: added quine + version: 1.02 + bug: variable recognition too greedy + fix: added 0-termination test + doc: added more quines + version: 1.03 + ui: changed el_splitc behaviour (thx dam) + bug: substitution allowed empty keys + fix: added checking code +20030408 + doc: added Paul's ultimate quine +20030409 + deps: moved to skalibs-0.23 + version: 1.04 +20030502 + deps: moved to skalibs-0.24 +20030514 + code: added getpid, removed pid support in execline + doc: documented it + doc: added links to el_substitute.html and co + packaging: added conf-compile, moved import to it + code: added -x to redirfd +20030519 + bug: wrong optind in exls1_elglob + fix: added "optind = 1" line +20030615 + deps: moved to skalibs-0.26 + packaging: exported exec, exit, runblock +20030620 + deps: moved to skalibs-0.27 (sigh) + packaging: updated scripts + code: added pushenv, popenv, and friends +20030725 + deps: moved to skalibs-0.29 + bug: possible bad pointer after realloc in substword() + fix: rewrote substword() interface to use stralloc *, not char * + internal: merged subsualloc.c with el_substitute.c +20030727 + internal: removed execline's pre-alloc'ed lex buffer (useless) + doc: documented pushenv and friends (at last) + version: 1.05 +20031008 + portability: reverted to old src/sys/print-ar.sh so that building + works on OpenBSD. Re-added btdep to sysdeps. Sigh. + ui: removed deprecated el_argv_* + internal: merged exls1_import[as].c + internal: created libexlp.a + code: added -u option to import and importas + code: added -S option to execline(b) + doc: reflected changes +20031107 + code: added stricter numeric arg checking (scan_uint0) + deps: moved to skalibs-0.34 +20040102 + deps: moved to skalibs-0.36 +20040229 + doc: added el_transform.html, removed el_split.html + doc: modified el_substitute.html, propagated changes + deps: moved to skalibs-0.40 + version: 1.06 +20041109 + code: added homeof +20070119 + code: added support for runblock -r 0 + ui: added -0 option to forbacktick and dollarat + ui: added -i option to import and importas + doc: reflected changes +20070122 + ui: added -i option to backtick + doc: reflected change + doc: suppressed hyperlinks in section headers +20070125 + deps: moved to skalibs-0.46 + version: 1.07 +20070917 + code: added tryexec +20081007 + code: added breakcode option to for and forbacktick + code: added ifte +20081020 + portability: export overwrites EXPORT on HFS+ (MacOS fs) + workaround: rename EXPORT to EXPORT- +20081022 + packaging: /command wasn't relative to conf-sp_root + fix: update package/upgrade + portability: strip -x is broken on some Solaris platforms + workaround: empty default conf-compile/conf-striplibs +20081023 + packaging: separated compat links, moved to new prepare... + doc: reflected ui changes + version: 1.08 +20091214 + code: added fdblock and ifelse + ui: added -X flag to if, ifte, ifthenelse +20100119 + code: added homeof, multidefine, loopwhilex, forx, forbacktickx + doc: reflected changes + bug: ifte didn't parse end of script correctly + fix: rewrite the incriminated part + deps: moved to skalibs-1.0.0 + packaging: moved to new system +20110628 + version: 1.1.0 +20110810 + build: libexecline objects were not built as PIC when required + fix: add x bits in deps-obj/ + version: 1.1.1 + ui: changed forx/forbacktickx behaviour when breakcode happens + version: 1.1.2 +20120214 + internal: changed bools from unsigned char to int + code: added -x option to if + doc: added multidefine to el_substitute.html's list +20120220 + version: 1.1.3 +20120312 + bug: typo in loopwhile.c, added in 1.1.3. Thx lory.fulgi@infinito.it. + fix: obvious. +20120313 + version: 1.1.4 +20120606 + code: added el_obsolescent() to mark loopwhile, for, forbacktick + code: changed forx, forbacktickx and backtick to not substitute + ui: added -E/-e options to those programs + doc: reflected changes +20120612 + version: 1.1.5 +20121111 + bug: background didn't set LASTPID + fix: correctly initialize the fmt array + version: 1.1.6 +20121115 + doc: properly documented if's -x option +20130124 + build: added FHS support + internal: switched to space quoter and null terminator (tm). + doc: reflected changes +20130212 + version: 1.2.0 +20130403 + bug: typo in fdreserve.c ('0' instead of 0) + impact: envvars weren't properly exported + fix: obvious + version: 1.2.1 +20130527 + bug: bad #ifdef in forx.c and backtick.c + impact: forx didn't work, bad USAGE in backtick + fix: obvious + ui: changed USAGE macros to execlineb syntax + version: 1.2.2 +20130913 + internal: fixed deprecated macros +20130927 + version: 1.2.3 +20131012 + bug: pipeline crashed on empty remainder + fix: don't try to be smart with df = 0, just die +20131019 + version: 1.2.4 +20131101 + ui: changed LASTPID to ! and LASTEXITCODE to ? + added EXECLINE_OLD_VARNAMES for compatibility +20131105 + version: 1.3.0 +20140325 + bug: segfault with empty remainder in backtick + fix: add a line to catch the case and exit + version: 1.3.1 +20140410 + build: moved to 4-number versioning + deps: moved to skalibs-1.6.0.0 +20140506 + ui: removed obsolete for, forbacktick, loopwhile + ui: removed default -DEXECLINE_DOSUBST_COMPAT +20140514 + version: 1.3.1.1 diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..63309ba --- /dev/null +++ b/COPYING @@ -0,0 +1,13 @@ +Copyright (c) 2011-2014 Laurent Bercot + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..9c6ae2f --- /dev/null +++ b/INSTALL @@ -0,0 +1,135 @@ +Build Instructions +------------------ + +* Requirements + ------------ + + - A POSIX-compliant C development environment + - GNU make version 3.81 or later + - skalibs version 2.0.0.0 or later: http://skarnet.org/software/skalibs/ + + This software will run on any operating system that implements +POSIX.1-2008, available at: + http://pubs.opengroup.org/onlinepubs/9699919799/ + + +* Standard usage + -------------- + + ./configure && make && sudo make install + + will work for most users. + It will install the binaries in /bin and the static libraries in +/usr/lib/execline. + + You can strip the binaries and libraries of their extra symbols via +"make strip" before the "make install" phase. It will shave a few bytes +off them. + + +* Customization + ------------- + + You can customize paths via flags given to configure. + See ./configure --help for a list of all available configure options. + + +* Environment variables + --------------------- + + Controlling a build process via environment variables is a big and +dangerous hammer. You should try and pass flags to configure instead; +nevertheless, the standard environment variables are recognized. + + The value of the CROSS_COMPILE environment variable will prefix the +building tools' names. The --enable-cross option is preferred, see +"Cross-compilation" below. + + If the CC environment variable is set, its value will override compiler +detection by configure. + + The values of CFLAGS, CPPFLAGS and LDFLAGS will be appended to flags +auto-detected by configure. To entirely override the flags set by +configure, use make -e. + + The value of LDLIBS will be appended by make to command lines that link +an executable, even without the -e option. + + The Makefile supports the DESTDIR convention for staging. + + +* Shared libraries + ---------------- + + Software from skarnet.org is small enough that shared libraries are +generally not worth using. Static linking is simpler and incurs less +runtime overhead and less points of failure: so by default, shared +libraries are not built and binaries are linked against the static +versions of the skarnet.org libraries. Nevertheless, you can: + * build shared libraries: --enable-shared + * link binaries against shared libraries: --disable-allstatic + + +* Static binaries + --------------- + + By default, binaries are linked against static versions of all the +libraries they depend on, except for the libc. You can enforce +linking against the static libc with --enable-static-libc. + + (If you are using a GNU/Linux system, be aware that the GNU libc +behaves badly with static linking and produces huge executables, +which is why it is not the default. Other libcs are better suited +to static linking, for instance musl: http://musl-libc.org/) + + execline is especially efficient when statically linked, so it +is recommended if you have a suitable libc. + + +* Cross-compilation + ----------------- + + skarnet.org packages centralize all the difficulty of +cross-compilation in one place: skalibs. Once you have +cross-compiled skalibs, the rest is easy. + + Use the --enable-cross=PREFIX option to configure, or simply +--enable-cross if your default toolchain is a cross-compiling +toolchain. And make sure to use the correct version of skalibs +for your target, and the correct sysdeps directory, making use +of the --with-include, --with-lib, --with-dynlib and --with-sysdeps +options as necessary. + + +* The slashpackage convention + --------------------------- + + The slashpackage convention (http://cr.yp.to/slashpackage.html) +is a package installation scheme that provides a few guarantees +over other conventions such as the FHS, for instance fixed +absolute pathnames. skarnet.org packages support it: use the +--enable-slashpackage option to configure, or +--enable-slashpackage=DIR for a prefixed DIR/package tree. +This option will activate slashpackage support during the build +and set slashpackage-compatible installation directories. +Other options setting individual installation directories will be +ignored. + + When using slashpackage, two additional Makefile targets are +available after "make install": + - "make update" changes the default version of the software to the +freshly installed one. (This is useful when you have several installed +versions of the same software, which slashpackage supports.) + - "make -L global-links" adds links from /command and /library.so to the +default version of the binaries and shared libraries. The "-L" option to +make is necessary because targets are symbolic links, and the default make +behaviour is to check the pointed file's timestamp and not the symlink's +timestamp. + + +* Out-of-tree builds + ------------------ + + skarnet.org packages do not support out-of-tree builds. They +are small, so it does not cost much to duplicate the entire +source tree if parallel builds are needed. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7d8901c --- /dev/null +++ b/Makefile @@ -0,0 +1,121 @@ +# +# This Makefile requires GNU make. +# +# Do not make changes here. +# Use the included .mak files. +# + +it: all + +CC = $(error Please use ./configure first) + +include package/targets.mak +include package/deps.mak +-include config.mak + +version_m := $(basename $(version)) +version_M := $(basename $(version_m)) +version_l := $(basename $(version_M)) +CPPFLAGS_ALL := -iquote src/include-local -Isrc/include $(CPPFLAGS) +CFLAGS_ALL := $(CFLAGS) -pipe -Wall +CFLAGS_SHARED := -fPIC +LDFLAGS_ALL := $(LDFLAGS) +LDFLAGS_SHARED := -shared +LDLIBS_ALL := $(LDLIBS) +REALCC = $(CROSS_COMPILE)$(CC) +AR := $(CROSS_COMPILE)ar +RANLIB := $(CROSS_COMPILE)ranlib +STRIP := $(CROSS_COMPILE)strip +INSTALL := ./tools/install.sh + +ALL_BINS := $(LIBEXEC_TARGETS) $(BIN_TARGETS) $(SBIN_TARGETS) +ALL_LIBS := $(SHARED_LIBS) $(STATIC_LIBS) +ALL_INCLUDES := $(wildcard src/include/$(package)/*.h) + +all: $(ALL_LIBS) $(ALL_BINS) $(ALL_INCLUDES) + +clean: + @exec rm -f $(ALL_LIBS) $(ALL_BINS) $(wildcard src/*/*.o src/*/*.lo) + +distclean: clean + @exec rm -f config.mak src/include/${package}/config.h + +tgz: distclean + @. package/info && \ + rm -rf /tmp/$$package-$$version && \ + cp -a . /tmp/$$package-$$version && \ + cd /tmp && \ + tar -zpcv --owner=0 --group=0 --numeric-owner --exclude=.git* -f /tmp/$$package-$$version.tar.gz $$package-$$version && \ + exec rm -rf /tmp/$$package-$$version + +strip: $(ALL_LIBS) $(ALL_BINS) +ifneq ($(strip $(ALL_LIBS)),) + exec ${STRIP} -x -R .note -R .comment -R .note.GNU-stack $(ALL_LIBS) +endif +ifneq ($(strip $(ALL_BINS)),) + exec ${STRIP} -R .note -R .comment -R .note.GNU-stack $(ALL_BINS) +endif + +install: install-dynlib install-libexec install-bin install-sbin install-lib install-include +install-dynlib: $(SHARED_LIBS:lib%.so=$(DESTDIR)$(dynlibdir)/lib%.so) +install-libexec: $(LIBEXEC_TARGETS:%=$(DESTDIR)$(libexecdir)/%) +install-bin: $(BIN_TARGETS:%=$(DESTDIR)$(bindir)/%) +install-sbin: $(SBIN_TARGETS:%=$(DESTDIR)$(sbindir)/%) +install-lib: $(STATIC_LIBS:lib%.a=$(DESTDIR)$(libdir)/lib%.a) +install-include: $(ALL_INCLUDES:src/include/$(package)/%.h=$(DESTDIR)$(includedir)/$(package)/%.h) + +ifneq ($(exthome),) + +update: + exec $(INSTALL) -l $(notdir $(home)) $(DESTDIR)$(exthome) + +global-links: $(DESTDIR)$(exthome) $(SHARED_LIBS:lib%.so=$(DESTDIR)$(sproot)/library.so/lib%.so) $(BIN_TARGETS:%=$(DESTDIR)$(sproot)/command/%) $(SBIN_TARGETS:%=$(DESTDIR)$(sproot)/command/%) + +$(DESTDIR)$(sproot)/command/%: $(DESTDIR)$(home)/command/% + exec $(INSTALL) -D -l ..$(exthome)/command/$( + + Please use the mailing-list for +questions about execline. diff --git a/README.macosx b/README.macosx new file mode 100644 index 0000000..d71a096 --- /dev/null +++ b/README.macosx @@ -0,0 +1,4 @@ + + This package will compile and run on Darwin (MacOS X), but the building of +shared libraries is not supported. + Make sure you use the --disable-shared option to configure. diff --git a/README.solaris b/README.solaris new file mode 100644 index 0000000..91a5b26 --- /dev/null +++ b/README.solaris @@ -0,0 +1,12 @@ + + This package assumes the existence of a POSIX shell in /bin/sh. + On Solaris, /bin/sh is not POSIX. Most versions of Solaris provide +a POSIX shell in /usr/xpg4/bin/sh. + + To compile this package on Solaris, you will need to run + + ./patch-for-solaris + + before you run ./configure. This script will change the #! invocation +of the configure script and various tools so that a POSIX shell is used +for the compilation process. diff --git a/configure b/configure new file mode 100755 index 0000000..b0c0e59 --- /dev/null +++ b/configure @@ -0,0 +1,385 @@ +#!/bin/sh + +usage () { +cat </dev/null 2>&1 && { echo "$1" ; return 0 ; } +$1 +EOF + echo "$1" | sed -e "s/'/'\\\\''/g" -e "1s/^/'/" -e "\$s/\$/'/" -e "s#^'\([-[:alnum:]_,./:]*\)=\(.*\)\$#\1='\2#" -e "s|\*/|* /|g" +} + +fail () { + echo "$*" + exit 1 +} + +fnmatch () { + eval "case \"\$2\" in $1) return 0 ;; *) return 1 ;; esac" +} + +cmdexists () { + type "$1" >/dev/null 2>&1 +} + +trycc () { + test -z "$CC_AUTO" && cmdexists "$1" && CC_AUTO=$1 +} + +stripdir () { + while eval "fnmatch '*/' \"\${$1}\"" ; do + eval "$1=\${$1%/}" + done +} + +tryflag () { + echo "checking whether compiler accepts $2 ..." + echo "typedef int x;" > "$tmpc" + if $CC_AUTO $CPPFLAGS_AUTO $CFLAGS_AUTO "$2" -c -o /dev/null "$tmpc" >/dev/null 2>&1 ; then + echo " ... yes" + eval "$1=\"\${$1} \$2\"" + eval "$1=\${$1# }" + return 0 + else + echo " ... no" + return 1 + fi +} + +tryldflag () { + echo "checking whether linker accepts $2 ..." + echo "typedef int x;" > "$tmpc" + if $CC_AUTO $CFLAGS_AUTO $LDFLAGS_AUTO -nostdlib "$2" -o /dev/null "$tmpc" >/dev/null 2>&1 ; then + echo " ... yes" + eval "$1=\"\${$1} \$2\"" + eval "$1=\${$1# }" + return 0 + else + echo " ... no" + return 1 + fi +} + + +# Actual script + +. package/info + +CC_AUTO="$CC" +CFLAGS_AUTO="$CFLAGS" +CPPFLAGS_AUTO="-D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=600 $CPPFLAGS" +LDFLAGS_AUTO="$LDFLAGS" +LDFLAGS_NOSHARED= +prefix= +exec_prefix='$prefix' +dynlibdir='$prefix/lib' +libexecdir='$exec_prefix/libexec' +bindir='$exec_prefix/bin' +sbindir='$exec_prefix/sbin' +libdir='$prefix/usr/lib/'$package +includedir='$prefix/usr/include' +sysdeps='$prefix/usr/lib/skalibs/sysdeps' +manualsysdeps=false +shared=false +static=true +slashpackage=false +sproot= +home= +exthome= +allstatic=true +evenmorestatic=false +addincpath='' +addlibspath='' +addlibdpath='' +vpaths='' +vpathd='' +cross="$CROSS_COMPILE" + +for arg ; do + case "$arg" in + --help) usage ;; + --prefix=*) prefix=${arg#*=} ;; + --exec-prefix=*) exec_prefix=${arg#*=} ;; + --dynlibdir=*) dynlibdir=${arg#*=} ;; + --libexecdir=*) libexecdir=${arg#*=} ;; + --bindir=*) bindir=${arg#*=} ;; + --sbindir=*) sbindir=${arg#*=} ;; + --libdir=*) libdir=${arg#*=} ;; + --includedir=*) includedir=${arg#*=} ;; + --with-sysdeps=*) sysdeps=${arg#*=} manualsysdeps=true ;; + --with-include=*) var=${arg#*=} ; stripdir var ; addincpath="$addincpath -I$var" ;; + --with-lib=*) var=${arg#*=} ; stripdir var ; addlibspath="$addlibspath -L$var" ; vpaths="$vpaths $var" ;; + --with-dynlib=*) var=${arg#*=} ; stripdir var ; addlibdpath="$addlibdpath -L$var" ; vpathd="$vpathd $var" ;; + --enable-shared|--enable-shared=yes) shared=true ;; + --disable-shared|--enable-shared=no) shared=false ;; + --enable-static|--enable-static=yes) static=true ;; + --disable-static|--enable-static=no) static=false ;; + --enable-allstatic|--enable-allstatic=yes) allstatic=true ;; + --disable-allstatic|--enable-allstatic=no) allstatic=false ;; + --enable-static-libc|--enable-static-libc=yes) evenmorestatic=true ;; + --disable-static-libc|--enable-static-libc=no) evenmorestatic=false ;; + --enable-slashpackage=*) sproot=${arg#*=} ; slashpackage=true ; ;; + --enable-slashpackage) sproot= ; slashpackage=true ;; + --disable-slashpackage) sproot= ; slashpackage=false ;; + --enable-cross=*) cross=${arg#*=} ;; + --enable-cross) cross= ;; + --disable-cross) cross= ;; + --enable-*|--disable-*|--with-*|--without-*|--*dir=*|--build=*) ;; + --host=*|--target=*) target=${arg#*=} ;; + -* ) echo "$0: unknown option $arg" ;; + *=*) ;; + *) target=$arg ;; + esac +done + +for i in prefix exec_prefix dynlibdir libexecdir bindir sbindir libdir includedir linkdynlibdir linkbindir linksbindir sysdeps sproot skalibs ; do + eval tmp=\${$i} + eval $i=$tmp + stripdir $i +done + +# Get usable temp filenames +i=0 +set -C +while : ; do + i=$(($i+1)) + tmpc="./tmp-configure-$$-$PPID-$i.c" + tmpe="./tmp-configure-$$-$PPID-$i.tmp" + 2>|/dev/null > "$tmpc" && break + 2>|/dev/null > "$tmpe" && break + test "$i" -gt 50 && fail "$0: cannot create temporary files" +done +set +C +trap 'rm -f "$tmpc" "$tmpe"' EXIT ABRT INT QUIT TERM HUP + +# Set slashpackage values +if $slashpackage ; then + home=${sproot}/package/${category}/${package}-${version} + exthome=${sproot}/package/${category}/${package} + if $manualsysdeps ; then + : + else + sysdeps=${sproot}/package/prog/skalibs/sysdeps + fi + binprefix=${home}/command + extbinprefix=${exthome}/command + dynlibdir=${home}/library.so + libexecdir=$binprefix + bindir=$binprefix + sbindir=$binprefix + libdir=${home}/library + includedir=${home}/include + while read dep ; do + addincpath="$addincpath -I${sproot}${dep}/include" + vpaths="$vpaths ${sproot}${dep}/library" + addlibspath="$addlibspath -L${sproot}${dep}/library" + if $allstatic ; then : ; else + vpathd="$vpathd ${sproot}${dep}/library.so" + addlibdpath="$addlibdpath -L${sproot}${dep}/library.so" + fi + done < package/deps-build +fi + +# Find a C compiler to use +echo "checking for C compiler..." +trycc ${cross}gcc +trycc ${cross}c99 +trycc ${cross}cc +test -n "$CC_AUTO" || { echo "$0: cannot find a C compiler" ; exit 1 ; } +echo " ... $CC_AUTO" +echo "checking whether C compiler works... " +echo "typedef int x;" > "$tmpc" +if $CC_AUTO $CPPFLAGS_AUTO $CFLAGS_AUTO -c -o /dev/null "$tmpc" 2>"$tmpe" ; then + echo " ... yes" +else + echo " ... no. Compiler output follows:" + cat < "$tmpe" + exit 1 +fi + +echo "checking target system type..." +test -n "$target" || target=$($CC_AUTO -dumpmachine 2>/dev/null) || target=unknown +echo " ... $target" +if test ! -d $sysdeps || test ! -f $sysdeps/target ; then + echo "$0: error: $sysdeps is not a valid sysdeps directory" + exit 1 +fi +if [ "x$target" != "x$(cat $sysdeps/target)" ] ; then + echo "$0: error: target $target does not match the contents of $sysdeps/target" + exit 1 +fi + +rt_lib=$(cat $sysdeps/rt.lib) +socket_lib=$(cat $sysdeps/socket.lib) +sysclock_lib=$(cat $sysdeps/sysclock.lib) +tainnow_lib=$(cat $sysdeps/tainnow.lib) +util_lib=$(cat $sysdeps/util.lib) + +tryflag CFLAGS_AUTO -std=c99 +tryflag CFLAGS_AUTO -fomit-frame-pointer +tryflag CFLAGS_AUTO -fno-exceptions +tryflag CFLAGS_AUTO -fno-unwind-tables +tryflag CFLAGS_AUTO -fno-asynchronous-unwind-tables +tryflag CFLAGS_AUTO -Wa,--noexecstack +tryflag CFLAGS_AUTO -fno-stack-protector +tryflag CPPFLAGS_AUTO -Werror=implicit-function-declaration +tryflag CPPFLAGS_AUTO -Werror=implicit-int +tryflag CPPFLAGS_AUTO -Werror=pointer-sign +tryflag CPPFLAGS_AUTO -Werror=pointer-arith +tryflag CPPFLAGS_AUTO -Wno-parentheses +tryflag CPPFLAGS_AUTO -Wno-uninitialized +tryflag CPPFLAGS_AUTO -Wno-missing-braces +tryflag CPPFLAGS_AUTO -Wno-unused-value +tryflag CPPFLAGS_AUTO -Wno-unused-but-set-variable +tryflag CPPFLAGS_AUTO -Wno-unknown-pragmas +tryflag CPPFLAGS_AUTO -Wno-pointer-to-int-cast + +if $evenmorestatic ; then + LDFLAGS_NOSHARED=-static +fi + +if $shared ; then + tryldflag LDFLAGS_AUTO -Wl,--hash-style=both +fi + +if test -z "$vpaths" ; then + while read dep ; do + base=$(basename $dep) ; + vpaths="$vpaths /usr/lib/$base" + addlibspath="$addlibspath -L/usr/lib/$base" + done < package/deps-build +fi + +CPPFLAGS_AUTO="$CPPFLAGS_AUTO $addincpath" +LDFLAGS_AUTO="$LDFLAGS_AUTO $addlibspath" +$allstatic || LDFLAGS_AUTO="$LDFLAGS_AUTO $addlibdpath" + +echo "creating config.mak..." +cmdline=$(quote "$0") +for i ; do cmdline="$cmdline $(quote "$i")" ; done +exec 3>&1 1>config.mak +cat << EOF +# This file was generated by: +# $cmdline +# Any changes made here will be lost if configure is re-run. + +target := $target +package := $package +prefix := $prefix +exec_prefix := $exec_prefix +dynlibdir := $dynlibdir +libexecdir := $libexecdir +bindir := $bindir +sbindir := $sbindir +libdir := $libdir +includedir := $includedir +sysdeps := $sysdeps +slashpackage := $slashpackage +sp_root := $sproot +version := $version +home := $home +exthome := $exthome +RT_LIB := ${rt_lib} +SOCKET_LIB := ${socket_lib} +SYSCLOCK_LIB := ${sysclock_lib} +TAINNOW_LIB := ${tainnow_lib} +UTIL_LIB := ${util_lib} + +CC := $CC_AUTO +CFLAGS := $CFLAGS_AUTO +CPPFLAGS := $CPPFLAGS_AUTO +LDFLAGS := $LDFLAGS_AUTO +LDFLAGS_NOSHARED := $LDFLAGS_NOSHARED +CROSS_COMPILE := $cross + +vpath lib%a$vpaths +EOF +if $allstatic ; then + echo ".LIBPATTERNS := lib%.a" + vpathd= +fi + echo "vpath lib%.so$vpathd" +echo +$static || echo "STATIC_LIBS :=" +$shared || echo "SHARED_LIBS :=" +exec 1>&3 3>&- +echo " ... done." + +echo "creating src/include/${package}/config.h..." +mkdir -p -m 0755 src/include/${package} +exec 3>&1 1> src/include/${package}/config.h +cat <&3 3>&- +echo " ... done." diff --git a/doc/background.html b/doc/background.html new file mode 100644 index 0000000..85d2589 --- /dev/null +++ b/doc/background.html @@ -0,0 +1,57 @@ + + + + + execline: the background command + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The background program

+ +background launches a command in the background, then goes on +with the execution flow. + +

Interface

+ +

+ In an execlineb script: +

+ +
+     background [ -d ] { prog1... } prog2...
+
+ +
    +
  • background reads a prog1... command in a +block and unquotes it.
  • +
  • It spawns a child executing prog1....
  • +
  • It sets the ! environment +variable to the pid of the prog1... process.
  • +
  • It then execs into prog2....
  • +
+ +

Options

+ +
    +
  • -d : doublefork. If the -d option is set, +prog1... will run as a grandchild of background.
  • +
+ +

Notes

+ +
    +
  • background prog1... "" prog2... is +equivalent to sh -c 'prog1... & ; exec prog2...'.
  • +
+ + + diff --git a/doc/backtick.html b/doc/backtick.html new file mode 100644 index 0000000..7bdc037 --- /dev/null +++ b/doc/backtick.html @@ -0,0 +1,66 @@ + + + + + execline: the backtick command + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The backtick program

+ +

+backtick runs a program and uses its output as the argument of +another program. +

+ +

Interface

+ +

+ In an execlineb script: +

+ +
+     backtick [ -i ] [ -n ] variable { prog1... } prog2...
+
+ +
    +
  • backtick reads prog1... in a +block and unquotes it.
  • +
  • It runs prog1... as a child process and saves its +output in memory. This output must not contain a null character.
  • +
  • backtick execs into the modified prog2..., with +variable added to the environment with prog1...'s +output as a value.
  • +
+ +

Options

+ +
    +
  • -i : insist. If prog1 exits non-zero, +backtick exits with the same exit code (or 111 if prog1 +crashed for some reason). Without this option, backtick execs into +prog2... no matter what prog1 does, with the null word as +variable's value if prog1 didn't write anything before +dying.
  • +
  • -n : chomp an ending newline off prog1...'s +output.
  • +
+ +

Notes

+ +
    +
  • You can start prog2... with "import variable unexport variable" +to perform variable substitution.
  • +
+ + + diff --git a/doc/cd.html b/doc/cd.html new file mode 100644 index 0000000..416dc42 --- /dev/null +++ b/doc/cd.html @@ -0,0 +1,45 @@ + + + + + execline: the cd command + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The cd program

+ +

+cd changes the current working directory to a +given directory, then executes a program. +

+ +

Interface

+ +
+     cd dir prog...
+
+ +

+cd performs a +chdir() +system call on dir, then execs into prog.... +

+ +

Notes

+ +

+cd is a standard shell builtin. Be careful if you want to +use the cd command outside of an execline script. +

+ + + diff --git a/doc/componentsb.txt b/doc/componentsb.txt new file mode 100644 index 0000000..a191f04 --- /dev/null +++ b/doc/componentsb.txt @@ -0,0 +1,41 @@ +#!/command/execlineb + +# This execlineb script will sleep for 1 second, then print some +# silly things on the standard output. + + +foreground # an unquoted string, evaluated to: foreground +{ # A single opening brace, not included in the argv + sleep 1 # Two unquoted strings, evaluated to " sleep" and " 1" + # (without the quotation marks). +} # A single closing brace, evaluated to the empty word + +"echo" # this is a quoted string. It will evaluate to the word: echo + +foo\ bar\ zoinx # This is one word, since the spaces are escaped +"foo bar zoinx" # This is exactly the same word, written another way + + " # this is not a comment, since it is inside a quoted string +# This is not a comment either \" # nor is this " # but this is one + +"\0x41\66\0103D\n" # This is the string ABCD followed by a newline. + # Be careful: the newline will be part of the word. + + \n # this is not a newline, but the single word: n + +$23 # This will NOT be replaced by anything with execline-1.y, unless + # substitution is explicitly asked for in the script. + # The dollar is no special character for the execline binary. + +baz"$1"qux # This will evaluate to the word baz$1qux +baz\$1qux # Same here +baz$1qux # Same here in execline-1.y + +${PATH} # This will NOT be replaced by execline ; use the import command + # if you need the $PATH value. + +'this is not a string' # it will be parsed as five separate words + +"\ +" # This will be parsed as the empty word. A (backslash, newline) + # sequence inside a quoted string is entirely removed. diff --git a/doc/define.html b/doc/define.html new file mode 100644 index 0000000..4d5e0f2 --- /dev/null +++ b/doc/define.html @@ -0,0 +1,42 @@ + + + + + execline: the define command + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The define program

+ +

+define replaces a literal with a value, then executes +another program. +

+ +

Interface

+ +
+     define [ -s ] [ -C | -c ] [ -n ] [ -d delim ] variable value prog...
+
+ + + + + diff --git a/doc/dieshdiedie.html b/doc/dieshdiedie.html new file mode 100644 index 0000000..dc3c661 --- /dev/null +++ b/doc/dieshdiedie.html @@ -0,0 +1,278 @@ + + + + + execline: why execline and not sh + + + + + + +execline
+Software
+skarnet.org

+ +

Why not just use /bin/sh ?

+ + + +

Security

+ +

+ One of the most frequent sources of security problems in programs +is parsing. Parsing is a complex operation, and it is easy to +make mistakes while designing and implementing a parser. (See +what Dan Bernstein says +on the subject, section 5.) +

+ +

+ But shells parse all the time. Worse, the essence +of the shell is parsing: the parser and the runner are intimately +interleaved and cannot be clearly separated, thanks to the +specification. +Even worse, the +shell sometimes has to perform double parsing, for instance +after parameter expansion. This can lead to atrocities like +

+zork="foo ; echo bar"
+touch $zork
+
not doing what you would like them to do, even in that simple +case. (zsh has a sane behaviour by +default, at the expense of explicitly breaking the spec.) +

+ +

+execlineb parses the script only once: when +reading it. The parser has been designed to be simple and systematic, +to reduce the potential for bugs - which you just cannot do +with a shell. After execlineb has split up the script into +words, no other parsing phase will happen, unless the user explicitly +requires it. Positional parameters, when +used, are never split, even if they contain spaces or newlines, unless +the user explicitly requires it. Users control exactly what +is split, what is done, and how. +

+ + +

Portability

+ +

+ The shell language was designed to make scripts portable across various +versions of Unix. But it is actually really hard to write a portable shell +script. There are dozens of distinct +sh flavours, not even counting the openly incompatible +csh approach and its various tcsh-like followers. +The ash, bash, ksh and zsh shells +all exhibit a different behaviour, even when they are +run with the so-called compatibility mode. From what I have +seen on various experiments, only zsh is able to follow the +specification to the letter, at the expense of being big and complex to +configure. This is a source of endless problems for shell script writers, +who should be able to assume that a script will run everywhere, +but cannot in practice. Even a simple utility like test +cannot be used safely with the normalized options, because most shells +come with a builtin test that does not respect the +specification to the letter. And let's not get started about echo, +which has its own set of problems. Rich Felker has +a page listing tricks +to use to write portable shell scripts. Writing a portable script should +not be that hard. +

+ +

+execline scripts are portable. There is no +complex syntax with opportunity to have an undefined or nonportable +behaviour. The execline package is portable across platforms: +there is no reason for vendors or distributors to fork their own +incompatible version. + Scripts will +not break from one machine to another; if they do, +it's not a "portability problem", it's a bug. You are then encouraged +to find the program that is responsible for the different behaviour, +and send a bug-report to the program author - including me, if the +relevant program is part of the execline distribution. +

+ +

+ A long-standing problem with Unix scripts is the shebang line, which +requires an absolute path to the interpreter. Scripts are only portable +as is if the interpreter can be found at the same absolute path on every +system. With /bin/sh, it is almost the case (Solaris +manages to get it wrong by having a non-POSIX shell as /bin/sh +and requiring something like #!/usr/xpg4/bin/sh to get a POSIX +shell to interpret your script). Other scripting languages are not so +lucky: perl can be /bin/perl, /usr/bin/perl, +/usr/local/bin/perl or something else entirely. For those cases, +some people advocate the use of env: #!/usr/bin/env perl. +But first, env can only find interpreters that can be found via the +user's PATH environment variable, which defeats the purpose of having an +absolute path in the shebang line in the first place; and second, this only +displaces the problem: the env utility does not +have a guaranteed absolute path. /usr/bin/env is the usual +convention, but not a strong guarantee: it is valid for systems to have +/bin/env instead, for instance. +

+ +

+execline suffers from the same issues. #!/bin/execlineb ? +#!/usr/bin/execlineb ? This is the only portability problem that +you will find with execline, and it is common to every script language. +

+ +

+ The real solution to this portability problem is a convention that +guarantees fixed absolute paths for executables, which the FHS does not do. +The slashpackage convention is +such an initiative, and is well-designed; but as with every +convention, it only works if everyone follows it, and unfortunately, +slashpackage has not +found many followers. Nevertheless, like every skarnet.org package, execline +can be configured to follow the slashpackage convention. +

+ + +

Simplicity

+ +

+ I originally wanted a shell that could be used on an embedded system. +Even the ash shell seemed big, so I thought of writing my +own. Hence I had a look at the +sh +specification... and ran away screaming. +This specification +is insane. It goes against every good programming +practice; it seems to have been designed only to give headaches +to wannabe sh implementors. +

+ +

+ POSIX cannot really be blamed for that: it only normalizes existing, historical +behaviour. One can argue whether it is a good idea to normalize atrocious +behaviour for historical reasons, as is the case with the infamous +gets +function, but this is the way it is. +

+ +

+ The fact remains that modern shells have to be compatible with that historical +nonsense and that makes them big and complex at best, or incompatible and ridden +with bugs at worst. +An OpenBSD developer said to me, when asked about the OpenBSD /bin/sh: +"It works, but it's far from not being a nightmare". +

+ +

+ Nobody should have +nightmare-like software on their system. Unix is simple. Unix +was designed to be simple. And if, as Dennis Ritchie said, "it takes a +genius to understand the simplicity", that's because incompetent people +took advantage of the huge Unix flexibility to write insanely crappy or +complex software. System administrators can only do a decent job when +they understand how the programs they run are supposed to work. People +are slowly starting to grasp this (or are they ? We finally managed +to get rid of sendmail and BIND, but GNU/Linux users seem happy to +welcome the era of D-Bus and systemd. Will we ever learn ?) - but even +sh, a seemingly simple and basic Unix program, is hard to +understand when you lift the cover. +

+ +

+ So I decided to forego sh entirely and take a new approach. So far it +has been working. + The execline specification is simple, and, +as I hope to have shown, easy to implement without too many bugs or +glitches. +

+ + +

Performance

+ +

+ Since it was made to run on an embedded system, execline was +designed to be light in memory usage. And it is. +

+ +
    +
  • No overhead due to interactive support.
  • +
  • No overhead due to unneeded features. Since every command performs +its task then executes another command, all occupied resources are instantly +freed. By contrast, a shell stays in memory during the whole execution +time.
  • +
  • Very limited use of the C library. Only the C interface to the +kernel's system calls, and some very basic functions like malloc(), +are used in the C library. In addition to avoiding the horrible interfaces +like stdio and the legacy libc bugs, this approach makes it easy +to statically compile execline - you will want to do that on an embedded +system, or just to gain performance.
  • +
+ +

+ You can have hundreds of execline scripts running simultaneously on an +embedded box. Not exactly possible with a shell. +

+ +

+ For scripts than do not require many computations that a shell can do +without calling external programs, + execline is faster than the shell. +Unlike sh's +one, the execline parser is simple and +straightforward; actually, it's more of a lexer than a parser, because +the execline language has been designed to be LL(1) - keep it simple, +stupid. +execline scripts get analysed and launched practically without a delay. +

+ + +
    +
  • + The best use case of execline is a linear, straightforward script, a +simple command line that does not require the shell's processing power. +In that case, execline will skip the shell's overhead and win big time +on resource usage and execution speed.
  • +
  • For longer scripts that fork a few commands, with a bit of +control flow, on average, an execline script will run at roughly the +same speed as the equivalent shell script, while using less resources.
  • +
  • The worst use case of execline is when the shell is used as a +programming language, and the script loops over complex internal constructs +that execline is unable to replicate without forking. In that case, +execline will waste a lot of time in fork/exec system calls that the +shell does not have to perform, and be noticeably slower. execline has +been designed as a scripting language, not as a programming +language: it is efficient at being the glue that ties together programs +doing a job, not at implementing a program's logic.
  • +
+ +
+

execline limitations

+ +
    +
  • execline can only handle scripts that fit in one argv. +Unix systems have a limit on the argv+envp size; +execline cannot execute scripts that are bigger than this limit.
  • +
  • execline commands do not perform signal handling. It is not +possible to trap signals inside an execline script. If you want to trap +signals, write a specific C program, or use a shell.
  • +
  • Due to the execline design, maintaining a state is +difficult. Information has to transit via environment variables or +temporary files, which makes commands like +loopwhilex a bit painful to handle.
  • +
  • Despite all its problems, the main shell advantage (apart from +being available on every Unix platform, that is) is that it +is often convenient. Shell constructs can be terse and short, +where execline constructs will be verbose and lengthy.
  • +
  • An execline script is generally heavier on execve() than +the average shell script - notably in programs where the shell can +use builtins. This can lead to a performance loss, especially when +executed programs make numerous calls to the dynamic linker: the system +ends up spending a lot of time resolving dynamic symbols. If it is a +concern to you, you should try and statically compile the +execline package, to eliminate the dynamic resolution costs. Unless +you're heavily looping around execve(), +the remaining costs will be negligible.
  • +
+ + + diff --git a/doc/dollarat.html b/doc/dollarat.html new file mode 100644 index 0000000..2c09592 --- /dev/null +++ b/doc/dollarat.html @@ -0,0 +1,86 @@ + + + + + execline: the dollarat command + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The dollarat program

+ +

+dollarat prints the positional parameters of an execline script. +

+ +

Interface

+ +
+     dollarat [ -n ] [ -0 | -d delimchar ]
+
+ +
    +
  • dollarat reads the number n of "positional +parameters" in the # environment variable. If that variable +is not set or does not contain a valid n, dollarat +exits 100.
  • +
  • dollarat prints the value of the 1 environment +variable, then delimchar, then the value of the 2 +environment variable... and so on until n. If one of +these variables is not set, dollarat exits 100.
  • +
  • If everything runs OK, dollarat exits 0. This makes it +one of the rare "exiting" execline commands.
  • +
+ +

Options

+ +
    +
  • -n : chomp. Do not print the last +delimchar.
  • +
  • -d delimchar : use the character +delimchar as separator between the arguments. Default: \n. +If delimchar has more than one character, only the first one is +used. If delimchar is the empty string, then dollarat +will output the positional parameters as a +sequence of netstrings (and the +-n option will be ignored).
  • +
  • -0 : use the null character as separator. Any -d +argument will be ignored. Warning: this option should only be used to feed +data to programs that know how to handle null-separated lists.
  • +
+ +

Notes

+ +
    +
  • You can use dollarat -d "" along with the +forbacktickx command to reliably loop +over the positional parameters: +
    + #!/command/execlineb
    + forbacktickx -d "" ARG { dollarat -d "" }
    + dosomething $ARG
    +
    + + will call dosomething in turn on each argument to the script. +That will work even if those arguments contain spaces, newlines, +or other fancy characters.
  • + +
  • Alternatively, instead of encoding data into a netstring, you can +use a null-separated list, which will work the same way: +
    + #!/command/execlineb
    + forbacktickx -0 ARG { dollarat -0 }
    + dosomething $ARG
    +
  • +
+ + + diff --git a/doc/el_pushenv.html b/doc/el_pushenv.html new file mode 100644 index 0000000..2ae16dd --- /dev/null +++ b/doc/el_pushenv.html @@ -0,0 +1,173 @@ + + + + + execline: pushing and popping the environment + + + + + + +

+execline
+Software
+skarnet.org +

+ +

Pushing and popping the environment

+ +

+ The execlineb launcher +can store positional +parameters, i.e. arguments given to your script, into the +environment. The # variable contains the number of arguments; +the 0 variable contains the name of your execline script; +the 1 variable contains the first argument; and so on. +

+ +

+ Up to execline-1.04, this could create problems with nested scripts: +the inner script would overwrite the outer script's parameters, and +there was no way to get them back. In particular, writing execline +commands in the execline language via the +runblock command was impossible. +

+ + + +

+ To solve that issue, execline now implements a kind of environment +stack. When execlineb reads the arguments, it does +not overwrite the positional parameters, but pushes them on a +stack: +

+ +
    +
  • # will be set to the current number of arguments
  • +
  • but if a variable named # existed before, it is renamed #:1
  • +
  • and if a variable named #:1 also existed, it is renamed #:2
  • +
  • ... and so on until #:n+1 doesn't exist.
  • +
+ +

+ Same goes for the other positional parameters. +

+ +

+ The script then runs; and commands such as +elgetpositionals use the current +frame of positional parameters, without paying attention to the deeper +levels. +

+ + + +

+ When you are done with the arguments, it is advisable to drop +the current frame, and pop the environment stack to get it back +to its previous state: +

+ +
    +
  • # will be unset
  • +
  • but if #:1 exists, it will be renamed #
  • +
  • and if #:2 exists, it will be renamed #:1
  • +
  • ... and so on until #:n+1 doesn't exist.
  • +
+ +

+ Again, same goes for the other positional parameters.
+The
runblock command will perform that +pop operation automatically; the standard "manual" way to +perform it is to use the emptyenv -P command. +

+ +

A pop example

+ +

+ Suppose you want to run the long-lived program prog after +printing the list of its arguments. +

+ +
+ #!/command/execlineb
+ elgetpositionals
+ foreground { echo $0 $@ }
+ prog $@
+
+ +

+will work, but will pollute prog's environment with a set of +positional parameters that have no meaning to it. A better script is: +

+ +
+ #!/command/execlineb
+ elgetpositionals
+ foreground { echo $0 $@ }
+ emptyenv -P
+ prog $@
+
+ +

+which will run prog with the same environment as the script's +caller. +

+ + + +

Substituting positional parameters without touching the environment

+ +

+ Most of the time, you just need to substitute the positional parameters +in your execline script, and don't need to go through the whole +elgetpositionals and +emptyenv chain. execline comes with an +integrated substitution mechanism, that does not touch the environment +at all: the -S n option. +

+ +

+ Scripts beginning with: +

+ +
+#!/command/execlineb -Sn
+foobar...
+
+ +

+ are equivalent to: +

+ +
+#!/command/execlineb
+elgetpositionals -Pn
+emptyenv -P
+foobar...
+
+ +

+ So, to summarize, from most efficient (but less flexible) to least efficient +(but more flexible): +

+ +
    +
  • Use execlineb -P if you don't need positional parameters +at all; for instance, in +s6 or +runit run scripts.
  • +
  • Use execlineb -Sn if you need only simple +positional parameter substitution in your script - no +shift or elgetopt, +no import 1.
  • +
  • Use execlineb -p, then elgetpositionals if +you don't mind overwriting the current stack of positional parameters.
  • +
  • Use execlineb, then elgetpositionals, then +emptyenv -P if you need the full power of positional parameter +handling.
  • +
+ + + diff --git a/doc/el_semicolon.html b/doc/el_semicolon.html new file mode 100644 index 0000000..615b411 --- /dev/null +++ b/doc/el_semicolon.html @@ -0,0 +1,124 @@ + + + + +execline: block management + + + + + + +

+execline
+Software
+skarnet.org +

+ +

Blocks

+ +

+A command line (and thus an execline script) is one-dimensional. But a +Unix execution flow can be two-dimensional: when two +instructions are sequenced, for instance. In that case, we need a +way to extract two command lines from one argv. +That is precisely what blocks are made for. +

+ +

+ execline commands that need more than one linear set of arguments +use blocks. For instance, the +foreground command needs to spawn a +first process, then execute into a second one. It reads the command +line for the first process from a block, and the command line for the +second process from the rest of the argv. In the following script: +

+
+ #!/command/execlineb
+ foreground { echo 1 } echo 2
+
+

+ echo 1 is read from a block and spawned; then +echo 2 is executed. +

+ +

execlineb syntax

+ +

+ In execlineb scripts, blocks are +delimited by braces. They can be nested. +

+ +

argv syntax

+ +

+ execlineb reads and parses the script, and converts it into an argv +(a simple Unix command line) with a different syntax for blocks. +In an argv, blocks are not delimited by braces; +they are made of quoted arguments and terminated by an +empty word (""). A quoted argument begins with a space. + Nested blocks are represented by arguments being +quoted several times, i.e. having several spaces in front of them; +an empty word inside a block +gets quoted too, i.e. it will be represented as a series of +spaces. +

+ +

+ Actually, the block-reading commands know nothing about braces; +they only understand the "quoted arguments + empty word" syntax. +So if you want to use foreground +from your shell to sequence echo 1 and +echo 2, you will have to write +

+ +
+ $ foreground ' echo' ' 1' '' echo 2
+
+ +

+ You do not really need to quote every argument inside a block in +that simple case. The following command works as well: +

+ +
+ $ foreground echo 1 '' echo 2
+
+ +

+ However, this is bad practice, because it leads to a security hole: +commands that perform +substitution inside a block may +produce empty words, which may modify your script's execution flow. +

+ +
+ $ define FOO '' foreground ' echo' ' ${FOO}' ' rm' ' -rf' ' /' '' echo blah
+
+ +

+ is safe, whereas +

+ +
+ $ define FOO '' foreground echo '${FOO}' rm -rf / '' echo blah
+
+ +

+ has very much unwanted results. (Don't try this at home.) +

+ +

+ You can use the EXECLINE_STRICT environment variable to +check proper block quoting. If that variable contains 1, +commands that read blocks will print a warning message everytime +they find an unquoted argument inside a block. If that variable +contains 2 or a bigger integer, commands will print an +error message and die on unquoted arguments. +
You can use execlineb's +-w or -W +switch to set EXECLINE_STRICT to 1 or 2. +

+ + + diff --git a/doc/el_substitute.html b/doc/el_substitute.html new file mode 100644 index 0000000..4da03f5 --- /dev/null +++ b/doc/el_substitute.html @@ -0,0 +1,309 @@ + + + + + execline: variable substitution + + + + + + +

+execline
+Software
+skarnet.org +

+ +

Variable substitution

+ +

+ In a shell, when you write +

+
+ $ A='foobar' ; echo $A
+
+

+ the echo command is given the argument foobar. +The foobar value has been substituted for the +A variable. +

+

+ Although execline maintains no state, and thus has no +real variables, it provides such a substitution facility +via substitution commands, namely: +

+ + +

+ A substitution command takes a key, i.e. a string +(which can contain any character but $, { and +}, although it is recommended to use only alphanumerical +characters), and a way to compute a value. +

+ +

Basics

+ +
    +
  • If the substitution key is foo, then the substitution +command will look for every occurrence of ${foo} or +$foo in the rest of its argv. Note that +${foo}bar matches, but $foobar +does not. To be safe, always use the syntax with +braces, unless $foo is a word on its own.
  • +
  • Every match is then replaced with the value.
  • +
+ +

+The simplest example is the following: +

+ +
+#!/command/execlineb
+define FOO blah
+echo $FOO
+
+ +

+ which will replace the FOO key with the blah value, +then execute the echo command. So that script will print +blah on stdout. +

+ + +

Quoting

+ +

+ execline allows you to write literal ${foo} constructs +even when the foo variable is being substituted, thanks to a +quoting mechanism. + Brace (pun intended) yourself: the following is the most complex part +of the whole language. +

+ +

Rationale

+ +

+ If we want to be able to have a literal ${foo}, then: +

+
    +
  • The ${foo} sequence will mean one of two things: +be substituted, or don't be substituted.
  • +
  • The default (unquoted) action should be: substitute.
  • +
  • A sequence that means "do not substitute" should be able +to appear literally. The quote character should also be able to +appear literally before a sequence that means "substitute". (Tricky, eh ?)
  • +
  • There should be as few quote characters as possible, to avoid +shell-like quoting nightmares.
  • +
+ +

Syntax

+ +

+ Rule: +

+ +
    +
  • The backslash (\) is a quote character for substitution commands.
  • +
  • The following rule applies only if the foo key is +explicitly used in a substitution command. If no command tries to +substitute anything for foo, sequences like +${foo} and preceding backslashes are left untouched.
  • +
  • (Substitute.) If ${foo} is preceded by 2*n backslashes +(an even number), the whole sequence will be +replaced with n backslashes, followed by the substituted value.
  • +
  • (Do not substitute.) If ${foo} is preceded by 2*n+1 backslashes +(an odd number), the whole sequence will be replaced +with n backslashes, followed by the literal ${foo}.
  • +
+ +

+ And now, the catch: the execlineb launcher, +as well as the shell, +interprets backslashes as escape characters. To make a word that contains +a backlash, you need to write two backslashes in your execline +script or shell command line. That means that the whole number of backslashes +you must write before your ${foo} sequence must be doubled +for the substitution command to read the proper number of backslashes and +perform its work correctly.
+ Once you keep that in mind, the quoting rule is logical. +

+ +

Example

+ +

+ The quoting rule is best illustrated with the following example, where +the A key is substituted, and the $B sequences mean +nothing special. +

+ +
+#!/command/execlineb
+define A val
+foreground { echo $A \\$A \\\\$A \\\\\\$A \\\\\\\\$A \\\\\\\\\\$A }
+             echo $B \\$B \\\\$B \\\\\\$B \\\\\\\\$B \\\\\\\\\\$B
+
+

+ prints +

+
+val $A \val \$A \\val \\$A
+$B \$B \\$B \\\$B \\\\$B \\\\\$B
+
+ +

+ Phew. +

+ + +

Value transformations

+
+ +

+ A value can go through +several transformations before it is +substituted. It can be crunched, +chomped, and/or +split. +

+ + +

Substitution of split values

+
+ +

+ A split value for FOO means that +a word containing ${FOO} will be replaced by zero, one, or +(usually) more than one word. The value actually means a +list of values. +

+ +

+ The rule is: substituting a list of values +(v1, v2, ...) for a key A is the +same as listing the substitutions of every value vi +for A.
+ For instance, +

+ +
+#!/command/execlineb
+define -s FOO "v1 v2 v3" echo prefix-${FOO}-postfix
+
+ +

+ will substitute three values for $FOO: v1, v2 +and v3. So the echo command will be called with three +arguments: prefix-v1-postfix, prefix-v2-postfix, and +prefix-v3-postfix. +

+ +

+(Implementation note: the fact that word prefixes are kept is +what makes execline's subtitutions secure. +Blocks are implemented via prefix +space characters; a substitution occurring inside a block will always produce +words beginning with the right amount of spaces, thus substituted +values cannot prematurely terminate a block.) +

+ + +

Recursive substitutions

+ +

+ A direct consequence of that rule is that substitutions will be performed +recursively if more than one key appears in one word and the values for +those keys are split. Parallel substitutions are performed from left to +right. For instance, in +

+ +
+#!/command/execlineb
+define -s B "1 2 3" echo ${B}x${B}
+
+

+ the ${B}x${B} word will be replaced with nine words: +1x1, 1x2, 1x3, 2x1, 2x2, +2x3, 3x1, 3x2, and 3x3, in that order. +
Here is an example with two distinct substitutions in parallel: +

+ +
+#!/command/execlineb
+multisubstitute
+{
+  define -s A "a b c d"
+  define -s B "1 2 3"
+}
+echo ${A}x${B}
+
+ +

+ The ${A}x${B} word will be replaced with twelve words: +ax1, ax2, ax3, bx1, bx2, +bx3, cx1, cx2, cx3, dx1, +dx2, and dx3, in that order. You can check that the +order of the define directives in +multisubstitute does not matter. +

+ +

+If the left-to-right order does not suit you, then you should perform +serial substitutions. For instance, the previous script can +be replaced with +

+ +
+#!/command/execlineb
+define -s B "1 2 3"
+define -s A "a b c d"
+echo ${A}x${B}
+
+

+ and will substitute ${B} first, then ${A}. So it +will print +

+ +
+ax1 bx1 cx1 dx1 ax2 bx2 cx2 dx2 ax3 bx3 cx3 dx3
+
+ +

+ in that order. +

+ + +

Not for the faint of heart

+ +

+ If you think you have mastered the art of execline substitution, then +you can try to do better than these people: +

+ + + + + diff --git a/doc/el_transform.html b/doc/el_transform.html new file mode 100644 index 0000000..094533a --- /dev/null +++ b/doc/el_transform.html @@ -0,0 +1,204 @@ + + + + + execline: value transformation + + + + + + +

+execline
+Software
+skarnet.org +

+ +

Value transformation

+ +

+ You can apply 3 kinds of transformations to a value which is to be +substituted for a variable: +crunching, chomping and splitting. They +always occur in that order. +

+ + + +

Delimiters

+
+ +

+ The transformations work around delimiters. Delimiters are +the semantic bounds of the "words" in your value. + You can use any character (except the null character, which you cannot +use in execline scripts) as a delimiter, by giving a string consisting +of all the delimiters you want as the argument to the -d option +used by substitution commands. By default, the string " \n\r\t" +is used, which means that the default delimiters are spaces, newlines, +carriage returns and tabs. +

+ + + +

Crunching

+
+ +

+ You can tell the substitution command to merge sets of consecutive +delimiters into a single delimiter. For instance, to replace +three consecutive spaces, or a space and 4 tab characters, with a +single space. This is called crunching, and it is done +by giving the -C switch to the substitution command. The +remaining delimiter will always be the first in the sequence. +

+ +

+ Crunching is mainly useful when also splitting. +

+ + +

Chomping

+
+ +

+ Sometimes you don't want the last delimiter in a value. + Chomping deletes the last character of a value if it is a +delimiter. It can be requested by giving the -n switch to the +substitution command. Note that chomping always happens after +crunching, which means you can use crunching+chomping to ignore, for +instance, a set of trailing spaces. +

+ + + +

Splitting

+
+ +

+ In a shell, when you write +

+ +
+ $ A='foo bar' ; echo $A
+
+ +

+ the echo command is given two arguments, foo +and bar. The $A value has been split, +and the space between foo and bar acted as a +delimiter. +

+ +

+If you want to avoid splitting, you must write something like +

+ +
+ $ A='foo bar' ; echo "$A"
+
+ +

+ The doublequotes "protect" the spaces. Unfortunately, it's easy +to forget them and perform unwanted splits during script execution +- countless bugs happen because of the shell's splitting behaviour. +

+ +

+ execline provides a splitting facility, with +several advantages over the shell's: +

+ +
    +
  • Splitting has to be explicitly requested, by specifying the +-s option to commands that perform +substitution. By default, +substitutions are performed as is, without interpreting the +characters in the value.
  • +
  • Positional parameters are never split, so that execline +scripts can handle arguments the way the user intended to. To +split $1, for instance, you have to ask for it +specifically: +
    +#!/command/execlineb -S1
    +define -sd" " ARG1S $1
    +blah $ARG1S
    +
    + and $ARG1S will be split using the space character as only delimiter. +
  • +
  • Any character can be a delimiter.
  • +
+ +

How it works

+ +
    +
  • A substitution command can request that the substitution value +be split, via the -s switch.
  • +
  • The splitting function parses the value, looking for delimiters. +It fills up a structure, marking the split points, and the number +n of words the value is to be split into. +
      +
    • A word is a sequence of characters in the value terminated +by a delimiter. The delimiter is not included in the word.
    • +
    • If the value begins with x delimiters, the word list +will begin with x empty words.
    • +
    • The last sequence of characters in the value will be recognized +as a word even if it is not terminated by a delimiter, unless you have +requested chomping and there was no delimiter at +the end of the value before the chomp operation - in which case +that last sequence will not appear at all.
    • +
  • +
  • The substitution rewrites the argv. A non-split value will +be written as one word in the argv; a split value will be written +as n separate words.
  • +
  • Substitution of split values is +performed recursively.
  • +
+ + + +

Decoding netstrings

+
+ +

+ Netstrings are +a way to reliably encode strings containing arbitrary characters. +execline takes advantage of this to offer a completely safe +splitting mechanism. If a substitution command is given an empty +delimiter string (by use of the -d "" option), the +splitting function will try to interpret the value as a sequence +of netstrings, every netstring representing a word. For instance, +in the following command line: +

+ +
+ $ define -s -d "" A '1:a,2:bb,0:,7:xyz 123,1: ,' echo '$A'
+
+ +

+ the echo command will be given five arguments: +

+ +
    +
  • the "a" string
  • +
  • the "bb" string
  • +
  • the empty string
  • +
  • the "xyz 123" string
  • +
  • the " " string (a single space)
  • +
+ +

+ However, if the value is not a valid sequence of netstrings, the +substitution command will die with an error message. +

+ +

+ The dollarat command, for instance, +can produce a sequence of netstrings (encoding all the arguments +given to an execline script), meant to be decoded by a substitution +command with the -d "" option. +

+ + + diff --git a/doc/elgetopt.html b/doc/elgetopt.html new file mode 100644 index 0000000..a5650f2 --- /dev/null +++ b/doc/elgetopt.html @@ -0,0 +1,60 @@ + + + + + execline: the elgetopt command + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The elgetopt program

+ +

+elgetopt performs getopt-style parsing on the +arguments to an execline script. +

+ +

Interface

+ +
+     elgetopt optstring prog...
+
+ +
    +
  • elgetopt expects to find a valid number n of +arguments in the # environment variable, and n+1 +environment variables 0, 1, ..., n. +It exits 100 if it is not the case.
  • +
  • elgetopt pushes +environment variables starting with ELGETOPT_. To get the +previous values back, use +emptyenv -o.
  • +
  • elgetopt looks into 1, 2... for options, +as specified by optstring, which is a standard getopt +string.
  • +
  • If the -c switch is recognized, elgetopt +sets the ELGETOPT_c environment variable. The value +of that variable is the argument to the -c switch if +it has one, and 1 otherwise.
  • +
  • After setting all recognized options, elgetopt makes +new #, 1, 2... "positional parameters" with +what remains.
  • +
  • elgetopt then execs into prog....
  • +
+ +

Notes

+ +
    +
  • GNU-style options are not supported.
  • +
+ + + diff --git a/doc/elgetpositionals.html b/doc/elgetpositionals.html new file mode 100644 index 0000000..d193413 --- /dev/null +++ b/doc/elgetpositionals.html @@ -0,0 +1,94 @@ + + + + + execline: the elgetpositionals command + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The elgetpositionals program

+ +

+elgetpositionals substitutes the positional parameters of an execline script. +

+ +

Interface

+ +
+     elgetpositionals [ -P sharp ] prog...
+
+ +
    +
  • elgetpositionals reads the number n of "positional +parameters" in the # environment variable. If that variable +is not set or does not contain a valid n, elgetpositionals +exits 100.
  • +
  • elgetpositionals performs some +substitutions in parallel on +prog...: +
      +
    • key: #, value: n
    • +
    • key: 0, value: the value of the 0 environment +variable
    • +
    • key: 1, value: the value of the 1 environment +variable
    • +
    • ... and so on until n (or sharp if it is +greater than n). Those values are never transformed.
    • +
    • key: @, value: all values of the variables from 1 to +n. This value is split +into n words.
    • +
    + If a variable between 0 and n does not +exist, elgetpositionals exits 100. +
  • +
+ +

Options

+ +
    +
  • -P sharp : substitute at least +sharp+1 positional parameters, from 0 to +max(n, sharp). If n<sharp, +positional parameters between n+1 and sharp are +replaced with the empty string. Not having the -P switch is +equivalent to having -P 0.
  • +
+ +

Notes

+ +
    +
  • A typical argument-taking execline script will +often begin that way: +
    + #!/command/execlineb
    + elgetopt optstring
    + elgetpositionals
    + prog...
    +
    +
  • +
  • If you are performing other substitutions that do not depend +on the positional parameters, think about replacing the +elgetpositionals call with a +multisubstitute call containing +the elgetpositionals directive.
  • +
  • If you are going to use the shift +command, it is best to use importas to +substitute the first positional parameters, then use shift, +then elgetpositionals. That way, $@ will correctly +be replaced by the remaining arguments. More generally, you should +try to use elgetpositionals as late as possible.
  • +
  • Use execlineb's -S switch instead of +elgetpositionals whenever you can. It is more efficient.
  • +
+ + + diff --git a/doc/elglob.html b/doc/elglob.html new file mode 100644 index 0000000..5b5d6aa --- /dev/null +++ b/doc/elglob.html @@ -0,0 +1,68 @@ + + + + + execline: the elglob command + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The elglob program

+ +

+elglob performs globbing on a pattern, then executes +another program. +

+ +

Interface

+ +
+     elglob [ -v ] [ -w ] [ -s ] [ -m ] [ -e ] [ -0 ] variable pattern prog...
+
+ +
    +
  • elglob performs +globbing +on pattern.
  • +
  • It then performs +variable substitution on +prog..., using variable as key and the result of the +globbing as value. The value is always split: it contains as many words +as they are matches for the globbing pattern.
  • +
  • elglob then execs into the modified prog....
  • +
+ +

Options

+ +
    +
  • -v : verbose. If there is a problem while globbing, print +a warning message on stderr.
  • +
  • -w : strict. If there is a problem while globbing, die +immediately. This is harsh - you probably don't need that option.
  • +
  • -s : sort the matches. By default, the results are +left unsorted.
  • +
  • -m : mark. Append a slash to each word that corresponds +to a directory.
  • +
  • -e : no escape. Treat backslashes in pattern +literally; do not allow quoting of metacharacters in pattern via +backslashes. Warning: the +execlineb launcher +uses the backslash as their own escape character - if you want a +backslash to be passed to elglob, do not forget to double +it.
  • +
  • -0 : null globbing. By default, if pattern +matches nothing, it will be substituted as is (verbatim in one word). With +this option, if pattern matches nothing, it will be properly +substituted as zero word.
  • +
+ + + diff --git a/doc/emptyenv.html b/doc/emptyenv.html new file mode 100644 index 0000000..81cf45a --- /dev/null +++ b/doc/emptyenv.html @@ -0,0 +1,57 @@ + + + + + execline: the emptyenv program + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The emptyenv program

+ +

+emptyenv empties the current environment, or cleans it up; then +executes a program. +

+ +

Interface

+ +
+     emptyenv [ -p ] prog...
+     emptyenv -c prog...
+     emptyenv [ -o ] [ -P ] prog...
+
+ +

+By default, emptyenv unsets all environment variables, then +execs into prog with its arguments. Options control which +environment variables are unset. +

+ +

Options

+ +
    +
  • -p : keep the PATH environment variable.
  • +
  • -c : clean up. Do not empty the environment. Instead, +remove every variable used internally by the execline programs, to avoid +any interference with or information leakage to external programs.
  • +
  • -o : pop environment +variables starting with ELGETOPT_. You might want to do this +before executing a final program from a script that uses +elgetpositionals.
  • +
  • -P : pop environment +variables starting with #, 0 to 9, and +EXECLINE_. You might want to do this before executing a final program +from a script launched by execlineb.
  • +
+ + + diff --git a/doc/exec.html b/doc/exec.html new file mode 100644 index 0000000..8655541 --- /dev/null +++ b/doc/exec.html @@ -0,0 +1,50 @@ + + + + + execline: the exec program + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The exec program

+ +

+exec executes the command line it is given. +

+ +

Interface

+ +
+     exec [ -c ] [ -l ] [ -a argv0 ]prog...
+
+ +

+exec execs into prog.... It does nothing else. +
Without options, exec can be seen as the execline NOP. +

+ +

Options

+ +
    +
  • -c : empty the environment. prog is executed with no environment variables. Warning: prog will run with an empty PATH, so make sure it does not rely on it.
  • +
  • -l : login. Prepends prog's argv[0] with a dash.
  • +
  • -a argv0 : argv0. Replace prog's argv[0] with argv0. This is done before adding a dash, if the -l option is also present.
  • +
+ +

+The exec command, along with its options, is designed to emulate +the standard exec shell builtin, which replaces the shell with the +command it is given. +

+ + + diff --git a/doc/execline-shell.html b/doc/execline-shell.html new file mode 100644 index 0000000..b3526f0 --- /dev/null +++ b/doc/execline-shell.html @@ -0,0 +1,53 @@ + + + + + execline: the execline-shell script + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The execline-shell script

+ +

+execline-shell executes $HOME/.execline-shell +with the arguments it is given. +

+ +

Interface

+ +
+     /etc/execline-shell
+
+ +
    +
  • execline-shell transforms itself into +${HOME}/.execline-shell $@.
  • +
  • ${HOME}/.execline-shell must be readable and +executable by the user. It must exec into an interactive +shell with $@ as its argument.
  • +
+ +

Notes

+ +
    +
  • execline-shell is meant to be used as the SHELL +environment variable value. It allows one to specify his favourite shell and +shell configuration in any language, since the ${HOME}/.execline-shell +file can be any executable program. ${HOME}/.execline-shell can be seen +as a portable .whateverrc file.
  • +
  • As an administrator-modifiable configuration file, execline-shell +provided in execline's etc/ subdirectory, and should be copied by +the administrator to /etc.
  • +
+ + + diff --git a/doc/execline-startup.html b/doc/execline-startup.html new file mode 100644 index 0000000..5df401d --- /dev/null +++ b/doc/execline-startup.html @@ -0,0 +1,59 @@ + + + + + execline: the execline-startup script + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The execline-startup script

+ +

+execline-startup performs some system-specific +login initialization, then executes ${HOME}/.execline-loginshell. +

+ +

Interface

+ +
+     /etc/execline-startup
+
+ +
    +
  • execline-startup sets the SHELL +environment variable to /etc/execline-shell. +It then performs some system-specific initialization, and +transforms itself into ${HOME}/.execline-loginshell $@.
  • +
  • ${HOME}/.execline-loginshell must be readable and +executable by the user. It must exec into $SHELL $@.
  • +
+ +

Notes

+ +
    +
  • execline-startup is an +execlineb script; hence, it is readable +and modifiable. It is meant to be modified by the system administrator +to perform system-specific login-time initialization.
  • +
  • As a modifiable configuration file, execline-startup is provided in execline's +etc/ subdirectory, and should be copied by the administrator +to /etc.
  • +
  • execline-startup is meant to be used as a login shell. +System administrators should manually add /etc/execline-startup +to the /etc/shells file. The /etc/execline-startup +file itself plays the role of the /etc/profile file, and +${HOME}/.execline-loginshell plays the role of the +${HOME}/.profile file.
  • +
+ + + diff --git a/doc/execlineb.html b/doc/execlineb.html new file mode 100644 index 0000000..64f29bd --- /dev/null +++ b/doc/execlineb.html @@ -0,0 +1,246 @@ + + + + + execline: the execlineb command + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The execlineb program

+ +

+execlineb reads and executes a script. +

+ +

Interface

+ +
+     execlineb [ -q | -w | -W ] [ -p | -P | -S nmin ] -c script [ args... ]
+
+ +

+or +

+ +
+     execlineb [ -q | -w | -W ] [ -p | -P | -S nmin ] scriptfile [ args... ]
+
+ +

+or in an executable file: +

+ +
+#!/command/execlineb [ -qwWpPSnmin ]
+script
+
+ +

+ Parsing phase. +

+ +
    +
  • execlineb reads and parses the script it is given. +It exits 100 on a syntax error and 111 on a temporary error. +It makes an argv, i.e. a system command line, with the +parsed script. If the argv is empty, execlineb +exits 0.
  • +
+ +

+ Environment management phase. +

+ +
    +
  • Pushing the current stack frame. If neither the +-p nor the -P nor the -S option is set: +execlineb pushes +the current positional parameters, i.e. environment variables that +start with #, 0, 1, ..., 9. +To get the previous values back, use +emptyenv -P.
  • +
  • Setting the new stack frame. If neither the -P +nor the -S option is set: +
      +
    • execlineb sets the # environment variable to +the number n of args it is given.
    • +
    • It sets the 0 environment variable to the name +of the script - or to the execlineb invocation name +if the -c option is used.
    • +
    • It sets the 1, 2, ... n +environment variables to the different args.
    • +
  • +
+ +

+ Execution phase. +

+ +
    +
  • execlineb executes into the argv it +has built from the script. +There is only one command line for the +whole script: the execlineb binary is a launcher, +whose sole purpose is to execute into that command line. It does +not stay in memory like a traditional interpreter would.
  • +
+ +

Options

+ +
    +
  • -c script : execute script, do not +look for a file.
  • +
+ +

+ See below for the other options. +

+ +

Syntax of scripts

+ +

+ An execlineb script is a string that must not contain the null character. +execlineb parses it and divides it into words. + + The parser recognizes the following components: +

+ +
    +
  • whitespace is defined as spaces, tabs, newlines and +carriage returns. Words are always separated by whitespace.
  • +
  • A quoted string begins with a doublequote (") +and ends with another doublequote. Quoted doublequotes must be prefixed +by a backslash (\). Quoted strings always evaluate to exactly +one word. For instance, "" evaluates to the empty word.
  • +
  • The \a, \b, \t, \n, \v, +\f, and \r sequences are recognized in quoted +strings, and are converted to the ASCII numbers 7, 8, 9, 10, 11, 12 and +13 respectively.
  • +
  • Inside a quoted string, backslashed +newlines disappear completely.
  • +
  • \0xab sequences are recognized in quoted strings +and evaluate to ASCII hexadecimal number ab.
  • +
  • \0abc sequences are recognized in quoted strings +and evaluate to ASCII octal number abc.
  • +
  • \abc sequences are recognized in quoted strings +and evaluate to ASCII decimal number abc. a must not +be zero.
  • +
  • A comment starts with a # and ends with the line. Comments +are not recognized inside quoted strings.
  • +
  • Anything else is an unquoted string, that can evaluate to +zero or more words.
  • +
  • Any character can be escaped in unquoted strings by prepending +it with a backslash. It works the same way in quoted strings, except +for the special sequences described above.
  • +
+ +

+ You can see an example of distinct execlineb components +here. +

+ +

+ In addition to that simple lexing, +execlineb performs the following higher-level parsing: +

+ +
    +
  • A word consisting of a single opening brace ({) +increments an internal level counter, blevel, and disappears from the +argv. Quoted open braces do not have that behaviour.
  • +
  • A word consisting of a single closing brace (}) +decrements blevel, and is replaced with the empty word. +Quoted closing braces do not have that behaviour.
  • +
  • If execlineb finds that braces are unmatched (i.e. +blevel goes below 0 during the parsing, or is not 0 at the end +of the script), it exits 100 with an error message.
  • +
  • execlineb automatically quotes +blocks. Which means that everytime it +finds a word, it prepends it with blevel spaces.
  • +
+ +

+For proper execution, the sequence of words must follow +the execline grammar. +

+ +

Options for block syntax checking

+ +

+ External execline commands that read blocks, like +foreground, use the EXECLINE_STRICT +environment variable: if it is set to 1, they will print a warning message +on stderr if they find their blocks not to be properly quoted. If it is set +to 2, they will also die. If it is set to 0, or unset, they won't complain +at all. +

+ +

+ Normally the EXECLINE_STRICT environment variable is +inherited from the caller. You can +force it unset, set to 1, or set to 2 by giving respectively the +-q, -w or -W option to execlineb. +

+ +

Options for environment management

+ +

+ Normally, execline scripts are reentrant: environment variables +potentially overwritten by execlineb, such as # or +0, are +pushed. This is the standard, safe +behaviour. Nevertheless, it is rather costly, and may be unneeded for +small scripts: for those cases, execline comes with two options +that bypass the environment management. Be warned that the purpose +of these options is optimization, and you should not +use them if you're not familiar with the way execlineb uses the +environment to store positional parameters. Alternatively, there's also +an integrated substitution mechanism that doesn't make use +of the environment at all. +

+ +
    +
  • The -p option will bypass the +push phase: the current frame of positional +parameters will be overwritten. The script will not be +reentrant.
  • +
  • The -P option will bypass positional parameter handling +completely: the environment will not be pushed, and positional +parameters will be ignored. execlineb -P -c "script" is +equivalent to, but more efficient than, execlineb -c +"emptyenv -P script". You should use the -P option +only in standalone scripts that take no arguments, such as +s6's or +runit's run scripts.
  • +
  • The -S nmin option will substitute the +positional parameters - up to at least nmin - but will not +push nor set environment +variables. execlineb -S3 -c "script" is equivalent to, +but more efficient than, execlineb -c "elgetpositionals -P3 emptyenv +-P script". See +the details.
  • +
+ +

Current limitations

+ +

+ execlineb builds and executes a unique +argv with the script: hence scripts are subject to OS-dependent +limitations such as the kernel buffer size for argv and envp + - at least 64 kB on most systems. This means that execlineb cannot +execute arbitrarily large scripts. Be careful with deeply nested scripts too: +without the -p/-P/-S option, each execlineb +invocation uses up some space in the environment. +

+ + + diff --git a/doc/exit.html b/doc/exit.html new file mode 100644 index 0000000..1e94ebd --- /dev/null +++ b/doc/exit.html @@ -0,0 +1,44 @@ + + + + + execline: the exit program + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The exit program

+ +

+exit exits with a given exit code. +

+ +

Interface

+ +
+     exit [ n ]
+
+ +

+exit exits with the exit code n, or 0 if n is not +given (in which case it's the same as true). If n is not +a number, exit exits 100. +

+ + +

Notes

+ +

+exit is a standard shell builtin, with the same function. +

+ + + diff --git a/doc/export.html b/doc/export.html new file mode 100644 index 0000000..35a7f68 --- /dev/null +++ b/doc/export.html @@ -0,0 +1,43 @@ + + + + + execline: the export program + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The export program

+ +

+export sets an environment variable to a given value, then +executes a program. +

+ +

Interface

+ +
+     export var value prog...
+
+ +

+export sets the var environment variable to +the string value, then execs into prog with +its arguments. +

+ +
    +
  • var must be given without a dollar !
  • +
  • var must not contain the character = .
  • +
+ + + diff --git a/doc/fdblock.html b/doc/fdblock.html new file mode 100644 index 0000000..5b2bfa8 --- /dev/null +++ b/doc/fdblock.html @@ -0,0 +1,55 @@ + + + + + execline: the fdblock program + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The fdblock program

+ +

+fdblock sets or unsets the O_NONBLOCK flag on a given file descriptor +(which makes reading or writing non-blocking or blocking), then executes +a program. +

+ +

Interface

+ +
+     fdblock [ -n ] fd prog...
+
+ +

+fdblock makes the file descriptor number fd blocking, +no matter what its previous state was. It then execs into prog +with its arguments. +

+ +

Options

+ +
    +
  • -n : non-blocking. Sets fd to non-blocking +mode instead of blocking mode. If used on stdin (0) or stdout (1), this +option will make a lot of command-line programs behave improperly, because +most simple command-line programs only support blocking stdin and stdout. +Make sure you know what you are doing.
  • +
+ +

Notes

+ +
    +
  • fdblock has no portable shell equivalent.
  • +
+ + + diff --git a/doc/fdclose.html b/doc/fdclose.html new file mode 100644 index 0000000..15265ac --- /dev/null +++ b/doc/fdclose.html @@ -0,0 +1,44 @@ + + + + + execline: the fdclose program + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The fdclose program

+ +

+fdclose closes a given file descriptor, then +executes a program. +

+ +

Interface

+ +
+     fdclose fd prog...
+
+ +

+fdclose closes the file descriptor number fd, then +execs into prog with its arguments. +

+ +

Notes

+ +
    +
  • fdclose n prog... is roughly equivalent to +sh -c 'exec prog... n<&-'
  • +
+ + + diff --git a/doc/fdmove.html b/doc/fdmove.html new file mode 100644 index 0000000..8498408 --- /dev/null +++ b/doc/fdmove.html @@ -0,0 +1,55 @@ + + + + + execline: the fdmove program + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The fdmove program

+ +

+fdmove moves or copies a given file descriptor, then +executes a program. +

+ +

Interface

+ +
+     fdmove [ -c ] fdto fdfrom prog...
+
+ +

+fdmove moves the file descriptor number fdfrom, +to number fdto, then execs into prog with its arguments. +If fdto is open, fdmove closes it before moving +fdfrom to it. +

+ +

Options

+ +
    +
  • -c : duplicate fdfrom to fdto +instead of moving it; do not close fdfrom.
  • +
+ +

Notes

+ +
    +
  • fdmove -c a b prog... is roughly equivalent to +sh -c 'exec prog... a>&b'
  • +
  • fdmove a b prog... is roughly equivalent to +sh -c 'exec prog... a>&b b<&-'
  • +
+ + + diff --git a/doc/fdreserve.html b/doc/fdreserve.html new file mode 100644 index 0000000..ef047dc --- /dev/null +++ b/doc/fdreserve.html @@ -0,0 +1,92 @@ + + + + + execline: the fdreserve program + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The fdreserve program

+ +

+fdreserve updates the environment with file descriptors that +are guaranteed safe to use, then executes a program. +

+ +

Interface

+ +
+     fdreserve n prog...
+
+ +
    +
  • fdreserve tries to reserve n file descriptors.
  • +
  • fdreserve sets the FD0, FD1, ..., +FDn-1 environment variables: each FDi contains a +valid file descriptor, that can be safely opened.
  • +
  • fdreserve then execs into prog with its arguments. +
+ +

Common use

+ +

+fdreserve can be used when you do not want to hardcode file +descriptors in your scripts. For instance, to create a pipe, you could +use: +

+ +
+ #!/command/execlineb
+ fdreserve 2
+ multisubstitute
+ {
+   importas fdr FD0
+   importas fdw FD1
+  }
+ piperw $fdr $fdw
+ prog...
+
+ +

+ Warning: fdreserve does not allocate descriptors, it merely returns +descriptors that are free at the time it is run. A program like +

+ +
+ #!/command/execlineb
+ fdreserve 3
+ multisubstitute
+ {
+   importas fdr FD0
+   importas fdw FD1
+ }
+ piperw $fdr $fdw
+ fdreserve 1
+ multisubstitute
+ {
+   importas oldfd FD2
+   importas newfd FD0
+ }
+ prog...
+
+ +

+may fail, because oldfd and newfd may be the same. +To avoid that, you should make sure that all descriptors returned by +fdreserve are actually allocated before calling fdreserve +again. +(Thanks to Paul Jarc for having +spotted that case.) +

+ + + diff --git a/doc/forbacktickx.html b/doc/forbacktickx.html new file mode 100644 index 0000000..9e09c81 --- /dev/null +++ b/doc/forbacktickx.html @@ -0,0 +1,77 @@ + + + + + execline: the forbacktickx command + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The forbacktickx program

+ +

+forbacktickx runs a program and uses its output as loop elements to +run another program. +

+ +

Interface

+ +

+ In an execlineb script: +

+ +
+     forbacktickx [ -p | -x breakcode ] [ -n ] [ -C | -c ] [ -0 | -d delim ] variable { gen... } loop...
+
+ +
    +
  • forbacktickx reads a +block, +gen..., and unquotes it.
  • +
  • It runs gen... as a child process. gen's +output must not contain a null character.
  • +
  • It reads gen's output as it needs, +splitting it automatically.
  • +
  • For every argument x in the split output, +forbacktickx runs loop... as a child process, with +variable=x added to its environment.
  • +
  • forbacktickx then exits 0. +
+ +

Options

+ +
    +
  • -p : parallel mode. Do not wait for a loop... +instance to finish before spawning the next one. forbacktickx will +still wait for all instances of loop to terminate before +exiting, though.
  • +
  • -0 : accept null characters from gen's output, +using them as delimiters. If this option and a -d option are +used simultaneously, the rightmost one wins.
  • +
  • -x breakcodes : breakcodes must +be a comma-separated list of exit codes. If at some point loop... +exits with a code listed in breakcodes, forbacktickx will not keep +looping, but will exit immediately with the same exit code. This doesn't apply +if the -p option has been given.
  • +
  • Other options are used to control +the substitution mechanism for every x. Of course, you can't +split x.
  • +
+ +

Notes

+ +
    +
  • You can start loop... with "import variable unexport variable" +to perform variable substitution. +
+ + + diff --git a/doc/foreground.html b/doc/foreground.html new file mode 100644 index 0000000..a2a465f --- /dev/null +++ b/doc/foreground.html @@ -0,0 +1,57 @@ + + + + + execline: the foreground command + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The foreground program

+ +

+foreground executes a sequence of commands. +

+ +

Interface

+ +

+ In an execlineb script: +

+ +
+     foreground { prog1... } prog2...
+
+ +
    +
  • foreground reads prog1 in a +block. It forks and +executes it, then waits for it to complete.
  • +
  • foreground sets the ? environment +variable to the exit code of prog1. If prog1... +did not exit normally, the ? value is 111.
  • +
  • foreground then execs into prog2....
  • +
+ +

Notes

+ +
    +
  • foreground is the basic sequence operator: it takes two +commands and executes them one by one. execline scripts require it to +wrap external commands that exit instead of natively supporting the +"perform some action, then execute some other program" model.
  • +
  • foreground prog1... "" prog2... is +equivalent to sh -c 'prog1... ; exec prog2...'. +
  • +
+ + + diff --git a/doc/forx.html b/doc/forx.html new file mode 100644 index 0000000..86729b8 --- /dev/null +++ b/doc/forx.html @@ -0,0 +1,66 @@ + + + + + execline: the forx command + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The forx program

+ +

+forx runs a loop. +

+ +

Interface

+ +

+ In an execlineb script: +

+ +
+     forx [ -p | -x breakcodes ] variable { args... } loop...
+
+ +
    +
  • forx reads a +block and unquotes it. +That block contains a list of args.
  • +
  • For each argument x in args..., +forx runs loop as a child process, with +variable=x added to its environment.
  • +
  • forx then exits 0.
  • +
+ +

Options

+ +
    +
  • -p : run in parallel. Do not wait for an instance of +loop... to exit before spawning the next one. forx +will still wait for all instances of loop to terminate before +exiting, though.
  • +
  • -x breakcodes : breakcodes must +be a comma-separated list of exit codes. If the -p flag +hasn't been given and loop exits with one of the codes in breakcodes, +forx will not run the following instances of the loop, but exit immediately with the +same exit code.
  • +
+ +

Notes

+ +
    +
  • You can start loop with "import variable unexport variable" +if you want variable substitution.
  • +
+ + + diff --git a/doc/getpid.html b/doc/getpid.html new file mode 100644 index 0000000..fb60c7c --- /dev/null +++ b/doc/getpid.html @@ -0,0 +1,44 @@ + + + + + execline: the getpid program + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The getpid program

+ +

+getpid stores its process ID in a given environment variable, +then executes a program. +

+ +

Interface

+ +
+     getpid var prog...
+
+ +

+getpid stores its PID in the var variable, then +execs into prog with its arguments. +

+ +

Notes

+ +
    +
  • var must be given without a dollar !
  • +
  • var must not contain =.
  • +
+ + + diff --git a/doc/grammar.html b/doc/grammar.html new file mode 100644 index 0000000..6c26dbd --- /dev/null +++ b/doc/grammar.html @@ -0,0 +1,160 @@ + + + + + execline: language design and grammar + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The execline language design and grammar

+ + +

execline principles

+ +

+ Here are some basic Unix facts: +

+ +
+ +

+ Knowing that, and wanting lightweight and efficient scripts, I +wondered: "Why should the interpreter stay in memory while the script +is executing ? Why not parse the script once and for all, put +it all into one argv, and just execute into that argv, +relying on external commands (which will be called from within the +script) to control the execution flow ?" +

+ +

execline was born.

+ +
    +
  • execline is the first script language to rely +entirely on chain loading. An execline script is a +single argv, made of a chain of programs designed to +perform their action then exec() into the next one.
  • +
  • The execlineb command is a +launcher: it reads and parses a text file, converting it +to an argv, then executes into that argv. It +does nothing more.
  • +
  • Straightforward scripts like nice -10 echo blah +will be run just as they are, without the shell overhead. +Here is what the script could look like: +
    +#!/command/execlineb -P
    +nice -10
    +echo blah
    +
    +
  • +
  • More complex scripts will include calls to other execline +commands, which are meant to provide some control over the process state +and execution flow from inside an argv.
  • +
+ + +

Grammar of an execline script

+ +

+An execline script can be parsed as follows: +

+ +
+ <instruction> = <> | external options <arglist> <instruction> | builtin options <arglist> <blocklist> <instruction>
+ <arglist> = <> | arg <arglist>
+ <blocklist> = <> | <block> <blocklist>
+ <block> = { <arglist> } | { <instrlist> }
+ <instrlist> = <> | <instruction> <instrlist>
+
+ +

+(This grammar is ambivalent, but much simpler to understand than the +non-ambivalent ones.) +

+ +
+ + +

execline features

+ +

+ execline commands can perform some transformations on +their argv, to emulate some aspects of a shell. Here are +descriptions of these features: +

+ + + + + diff --git a/doc/heredoc.html b/doc/heredoc.html new file mode 100644 index 0000000..f49f880 --- /dev/null +++ b/doc/heredoc.html @@ -0,0 +1,56 @@ + + + + + execline: the heredoc program + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The heredoc program

+ +

+heredoc runs a command with a certain string fed to a +file descriptor. +

+ +

Interface

+ +
+     heredoc [ -d ] fd string prog...
+
+ +
    +
  • heredoc execs into prog... with +string available on the fd file +descriptor.
  • +
  • string must not contain a null character.
  • +
+ +

Options

+ +
    +
  • -d : run the process feeding string to fd +as a grandchild of heredoc. This is meant to prevent a zombie +from hanging around if prog... has read string and fails +to wait for its children.
  • +
+ +

Notes

+ +
    +
  • heredoc is meant to be used in place of the shell +<< construct, which includes here-documents +into scripts.
  • +
+ + + diff --git a/doc/homeof.html b/doc/homeof.html new file mode 100644 index 0000000..31f2a40 --- /dev/null +++ b/doc/homeof.html @@ -0,0 +1,37 @@ + + + + + execline: the homeof program + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The homeof program

+ +

+homeof prints the home directory of a user. +

+ +

Interface

+ +
+     homeof user
+
+ +

+homeof finds the name of user's home directory, writes +it on stdout, then exits 0. If an error occurs, it prints nothing on +stdout but exits 111 with an explanatory message on stderr. +

+ + + diff --git a/doc/if.html b/doc/if.html new file mode 100644 index 0000000..2157a07 --- /dev/null +++ b/doc/if.html @@ -0,0 +1,68 @@ + + + + + execline: the if command + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The if program

+ +

+if performs conditional execution. +

+ +

Interface

+ +

+ In an execlineb script: +

+ +
+     if [ -X ] [ -n ] [ -t | -x exitcode ] { prog1... } prog2...
+
+ +
    +
  • if reads prog1... in a +block. It forks and executes it, +then waits for it to complete.
  • +
  • If prog1 crashes, if exits 1 with a special +error message.
  • +
  • If prog1 exits a non-zero status, +if exits 1.
  • +
  • Else if execs into prog2.
  • +
+ +

Options

+ +
    +
  • -X : treat a crash of prog1 as a non-zero ("false") exit. +
  • -n : negate the test (exit on true, exec into prog2 on false)
  • +
  • -x exitcode : exit exitcode instead of 1 if the test fails.
  • +
  • -t : exit 0 instead of 1 if the test fails. +This is equivalent to -x 0.
  • +
+ +

Notes

+ +
    +
  • if will exit if prog1... exits false. To use it in +an execline script that must run prog3... no matter the result of +the test, use a foreground wrapper: +
     foreground { if { prog1... } prog2... } prog3... 
    +(in execlineb syntax)
  • +
  • if prog1... "" prog2... is +equivalent to sh -c 'prog1... && exec prog2...'.
  • +
+ + + diff --git a/doc/ifelse.html b/doc/ifelse.html new file mode 100644 index 0000000..2298cf8 --- /dev/null +++ b/doc/ifelse.html @@ -0,0 +1,59 @@ + + + + + execline: the ifelse command + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The ifelse program

+ +

+ ifelse performs conditional execution, with two branches. +

+ +

Interface

+ +

+ In an execlineb script: +

+
+     ifelse [ -X ] [ -n ] { prog1... } { prog2... } prog3...
+
+ +
    +
  • ifelse reads prog1... in a +block. It forks and executes it, +then waits for it to complete.
  • +
  • If prog1 crashes, ifelse exits 1 with an error message.
  • +
  • If prog1 exits with a return code equal to 0, +ifelse execs into prog2.
  • +
  • Else ifelse execs into prog3.
  • +
+ +

Options

+ +
    +
  • -n : negate the test.
  • +
  • -X : do not die if prog1 crashes; treat a crash +as a non-zero ("false") exit.
  • +
+ +

Notes

+ +
    +
  • ifelse prog1... "" prog2... "" prog3... is +roughly equivalent to sh -c 'prog1... && exec prog2... || exec prog3...'.
  • +
+ + + diff --git a/doc/ifte.html b/doc/ifte.html new file mode 100644 index 0000000..40b0ec9 --- /dev/null +++ b/doc/ifte.html @@ -0,0 +1,67 @@ + + + + + execline: the ifte command + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The ifte program

+ +

+ifte performs a conditional alternative. +

+ +

Interface

+ +

+ In an execlineb script: +

+ +
+     ifte [ -X ] [ -n ] { progthen... } { progelse... } progif...
+
+ +
    +
  • ifte reads progthen... and progelse... in two +consecutive blocks.
  • +
  • ifte runs progif... as a child process +and waits for it to complete.
  • +
  • If progif... crashes (i.e. is killed by a signal), ifte +exits 1 with an error message.
  • +
  • If progif... exits zero, ifte execs into +progthen..., else it execs into progelse....
  • +
+ +

Options

+ +
    +
  • -X : do not exit if progif crashes; instead, +proceed as if the test had returned false.
  • +
  • -n : negate the test. progthen... will be run +iff progif... exits nonzero.
  • +
+ +

Notes

+ +

+ ifte is a simpler version of ifthenelse. +It performs only conditional execution, not instruction sequence. +

+ +

+"ifthenelse { progif } { progthen } { progelse } remainder" is the +equivalent of "foreground { ifte { progthen } { progelse } progif } remainder". +

+ + + diff --git a/doc/ifthenelse.html b/doc/ifthenelse.html new file mode 100644 index 0000000..3180412 --- /dev/null +++ b/doc/ifthenelse.html @@ -0,0 +1,59 @@ + + + + + execline: the ifthenelse command + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The ifthenelse program

+ +

+ifthenelse performs a conditional alternative. +

+ +

Interface

+ +

+ In an execlineb script: +

+ +
+     ifthenelse [ -X ] [ -s ] { progif... } { progthen... } { progelse... } prog...
+
+ +
    +
  • ifthenelse reads +progif..., progthen... and progelse... in 3 +consecutive blocks.
  • +
  • ifthenelse runs progif... as a child process +and waits for it to complete.
  • +
  • If progif... crashes (i.e. is killed by a signal), ifthenelse +exits 1 with an error message.
  • +
  • If progif... exits zero, ifthenelse runs +progthen... as a child process, else it runs progelse....
  • +
  • ifthenelse waits for its child to complete and puts the exit +status in the ? environment variable. It then +execs into prog....
  • +
+ +

Options

+ +
    +
  • -X : if progif crashes, do not exit; proceed +as if it had returned false.
  • +
  • -s : magic scoping hack. This option does powerful but +ugly things, and is left undocumented on purpose.
  • +
+ + + diff --git a/doc/import.html b/doc/import.html new file mode 100644 index 0000000..1c6896c --- /dev/null +++ b/doc/import.html @@ -0,0 +1,37 @@ + + + + + execline: the import program + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The import program

+ +

+import replaces an environment variable name with its value, +then executes another program. +

+ +

Interface

+ +
+     import [ -i | -D default ] [ -s ] [ -C | -c ] [ -n ] [ -d delim ] envvar prog...
+
+ +
    +
  • import behaves exactly as +importas envvar envvar. +
+ + + diff --git a/doc/importas.html b/doc/importas.html new file mode 100644 index 0000000..a9c6f15 --- /dev/null +++ b/doc/importas.html @@ -0,0 +1,57 @@ + + + + + execline: the importas program + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The importas program

+ +

+importas replaces a literal with the value of an +environment variable, then executes another program. +

+ +

Interface

+ +
+     importas [ -i | -D default ] [ -s ] [ -C | -c ] [ -n ] [ -d delim ] variable envvar prog...
+
+ +
    +
  • importas fetches the value of envvar in the +environment. If neither the -D nor the -i option is given, +and envvar is undefined, the null word is returned.
  • +
  • importas then performs +variable substitution on prog..., +with variable as key and that string as value. +
  • importas then execs into the modified prog....
  • +
+ +

Options

+ +
    +
  • -D default : If envvar is +undefined, and this option is not given, substitute zero word for +variable instead of the empty word; and if it is given, +substitute default instead. To substitute the empty word, +use -D "".
  • +
  • -i : Insist. If envvar is undefined, +importas will not do anything; instead, it will exit 100 with an +error message. This has precedence over any -D option.
  • +
  • Other options are used to control +the substitution mechanism.
  • +
+ + + diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 0000000..682e935 --- /dev/null +++ b/doc/index.html @@ -0,0 +1,213 @@ + + + + + execline: a small scripting language + + + + + + +

+Software
+skarnet.org +

+ +

execline

+ +

What is it ?

+ +

+ execline is a (non-interactive) scripting language, like sh ; +but its syntax is quite different from a traditional shell syntax. +The execlineb program is meant to be used as an interpreter for a +text file; the other commands are essentially useful inside an +execlineb script. +

+ +

+ execline is as powerful as a shell: it features +conditional loops, +getopt-style option handling, +filename globbing, and more. + Meanwhile, its syntax is far more logic and predictable than the +shell's syntax, and has no security issues. +

+ + + +
+ +

Installation

+ +

Requirements

+ +
    +
  • A POSIX-compliant system with a standard C development environment
  • +
  • GNU make, version 3.81 or later
  • +
  • skalibs version +2.0.0.0 or later
  • +
+ +

Licensing

+ +

+ execline is free software. It is available under the +ISC license. +

+ +

Download

+ +
    +
  • The current execline version is 2.0.0.0.
  • +
+ +

Compilation

+ +
    +
  • See the enclosed INSTALL file for installation details.
  • +
+ +

Upgrade notes

+ +
    +
  • This page lists the differences to be aware of between +the previous versions of execline and the current one.
  • +
+ +
+ +

Special note

+ +

+ Before version 2.0.0.0, execline used the slashpackage convention by default. + This is not the case anymore; nevertheless, the examples in this documentation +still use #!/command/execlineb as their shebang line, and assume that +the execline binaries are available in /command. Adapt them according +to your installation: the shebang lines for your system might be something like +#!/bin/execlineb, or #!/usr/bin/execlineb, or +#!/usr/local/bin/execlineb, or something else entirely. +

+ +
+ +

Reference

+

Commands

+ +

+ All these commands exit 111 if they encounter a temporary error, and +100 if they encounter a permanent error - such as a misuse. +

+

+ (Script parser / launcher) +

+ +

+ (Process state control) +

+ +

+ (Basic block management) +

+ +

+ (Variable management) +

+ +

+ (Loops) +

+ +

+ (Positional parameters and options management) +

+ +

+ (Miscellaneous) +

+ + +

Provided scripts: example .profile replacement

+ + + +

Fun stuff

+ + + +

Related resources

+ +
    +
  • execline is discussed on the +skaware mailing-list.
  • +
+ + + diff --git a/doc/loopwhilex.html b/doc/loopwhilex.html new file mode 100644 index 0000000..9def60f --- /dev/null +++ b/doc/loopwhilex.html @@ -0,0 +1,60 @@ + + + + + execline: the loopwhilex command + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The loopwhilex program

+ +

+loopwhilex performs a conditional loop. +

+ +

Interface

+ +
+     loopwhilex [ -n ] [ -x exitcodes ] prog...
+
+ +
    +
  • loopwhilex runs prog... as a child process and +waits for it to complete.
  • +
  • As long as prog exits zero, loopwhile runs it again.
  • +
  • loopwhilex then exits 0. If prog was killed by a signal, +loopwhilex exits that signal's number instead.
  • +
+ +

Options

+ +
    +
  • -x exitcodes : exitcodes must be a comma-separated +list of valid exit codes. If this option is given, loopwhilex will exit if prog...'s +exit code is listed in breakcodes.
  • +
  • -n : negate the test: run prog... as long as it exits non-zero +(or exits a code that is not listed in breakcodes).
  • +
+ +

Notes

+ +
    +
  • loopwhilex prog... is equivalent to loopwhilex -n -x 0 prog....
  • +
  • Be careful: execline maintains no state, in particular it +uses no real variables, and environment will +be of no use here since every instance of prog... runs as a separate +child process. To avoid being stuck in an infinite loop, prog... +should modify some external state - for instance, the filesystem.
  • +
+ + + diff --git a/doc/multidefine.html b/doc/multidefine.html new file mode 100644 index 0000000..03f6b22 --- /dev/null +++ b/doc/multidefine.html @@ -0,0 +1,69 @@ + + + + + execline: the multidefine command + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The multidefine program

+ +

+multidefine splits a value and defines several variables at once, +then executes another program. +

+ +

Interface

+ +

+ In an execlineb script: +

+ +
+     multidefine [ -0 ] [ -r ] [ -C | -c ] [ -n ] [ -d delim ] value { variables... } prog...
+
+ +
    +
  • multidefine reads a block +containing a list of variables.
  • +
  • multidefine splits +value, and performs other operations depending on the given +options.
  • +
  • multidefine performs +parallel substitution on +prog..., using all of the variables in the block as keys. +The first word in the split value is assigned to the first +variable, the second word is assigned to the second variable, +and so on. Every variable is substituted with exactly one word.
  • +
  • If a variable is the empty word, then the word in the split +value corresponding to its position is not substituted. So you can +use empty words to pad the list of variables and only perform substition +on the relevant fields.
  • +
  • multidefine then execs into the modified prog....
  • +
+ +

Options

+ +
    +
  • -0 : if there are more variables in the block than +there are words in the split value, the excess variables +will be replaced with zero word. Without this option, the excess variables are +replaced with the empty word.
  • +
  • -r : behave similarly to the "read" shell command. +If there are more words in the split value than there are +variables in the block, the last variable will be replaced with all +the remaining words (and will be split). Without this option, the last variable +is replaced with a single word, and the excess words are lost.
  • +
+ + + diff --git a/doc/multisubstitute.html b/doc/multisubstitute.html new file mode 100644 index 0000000..6400187 --- /dev/null +++ b/doc/multisubstitute.html @@ -0,0 +1,121 @@ + + + + + execline: the multisubstitute command + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The multisubstitute program

+ +

+multisubstitute performs several substitutions at once in +its argv, then executes another program. +

+ +

Interface

+ +

+ In an execlineb script: +

+ +
+     multisubstitute
+     {
+       [ define [ -n ] [ -s ] [ -C | -c ] [ -d delim ] variable value ]
+       [ importas [ -i | -D default ] [ -n ] [ -s ] [ -C | -c ] [ -d delim ] variable envvar ]
+       [ import [ -i | -D default ] [ -n ] [ -s ] [ -C | -c ] [ -d delim ] envvar ]
+       [ elglob [ -v ] [ -w ] [ -s ] [ -m ] [ -e ] [ -0 ] variable pattern ]
+       [ elgetpositionals [ -P sharp ] ]
+       [ multidefine value { variable... } ]
+       ...
+     }
+     prog...
+
+ +
    +
  • multisubstitute reads a block +containing a series of substitution commands. It performs all +those substitutions on +prog... in parallel. Check the relevant documentation page +to learn about the syntax of each substitution command.
  • +
  • multisubstitute then execs into the modified prog....
  • +
+ +

Options

+ +
    +
  • If a backtick directive was given with the -i option, +and command crashes or exits nonzero, multisubstitute will +also exit with the same exit code.
  • +
  • If an import or importas directive was given with the +-i option, and the looked up variable is undefined, +multisubstitute will exit 100.
  • +
+ +

Rationale

+ +

Security

+ +

+ multisubstitute can be used to avoid unwanted +serial substitutions. Consider the following script: +

+ +
+ #!/command/execlineb
+ export A wrong
+ define B ${A}
+ import A
+ echo ${B}
+
+ +

+ Running it will print wrong, because A is substituted +after B. On the contrary, the following script: +

+ +
+ #!/command/execlineb
+ export A wrong
+ multisubstitute
+ {
+   define B ${A}
+   import A
+ }
+ echo ${B}
+
+ +

+ will print ${A}, because A and B are substituted at the same +time. Serial substitution may be what you want - but when in doubt, +always perform parallel substitution. +

+ +

Efficiency

+ +

+Substitution is a costly mechanism: +the whole argv is read three times and rewritten twice. +Serial substitution multiplies the cost by the number of +substitutions, whereas parallel substitution pays the price only once. +

+ +

Credits

+ +

+Paul Jarc first originated the +idea of the multisubstitute command and a possible syntax. +

+ + + diff --git a/doc/pipeline.html b/doc/pipeline.html new file mode 100644 index 0000000..4f6677f --- /dev/null +++ b/doc/pipeline.html @@ -0,0 +1,67 @@ + + + + + execline: the pipeline command + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The pipeline program

+ +

+pipeline runs two commands with a pipe between them. +

+ +

Interface

+ +

+ In an execlineb script: +

+ +
+     pipeline [ -d ] [ -r | -w ] { prog1... } prog2...
+
+ +
    +
  • pipeline reads prog1... in a +block and unquotes it.
  • +
  • It runs prog1... as a child process and execs into +prog2..., with a pipe between prog1's stdout and +prog2's stdin.
  • +
  • prog1's pid is available in prog2 as the ! +environment variable.
  • +
+ +

Options

+ +
    +
  • -d : run prog1... +as a grandchild of pipeline. This is meant to prevent a zombie +from hanging around if prog2... fails to wait for its children.
  • +
  • -r : make prog1... the writer and +prog2... the reader. This is the default.
  • +
  • -w : make prog1... the reader and +prog2... the writer.
  • +
+ +

Notes

+ +
    +
  • You can easily create a chain of pipes: pipeline a "" pipeline b "" c +is roughly equivalent to +sh -c 'exec a | b | c', except that shells usually run c +as a child process like a and b, and exec has no +effect.
  • +
+ + + diff --git a/doc/piperw.html b/doc/piperw.html new file mode 100644 index 0000000..64b9477 --- /dev/null +++ b/doc/piperw.html @@ -0,0 +1,38 @@ + + + + + execline: the piperw command + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The piperw program

+ +

+piperw creates a pipe (an anonymous one), then +executes a program. +

+ +

Interface

+ +
+     piperw fdr fdw prog...
+
+ +

+piperw creates a pipe with descriptor fdw as the +writing end and descriptor fdr as the reading end. +It then execs into prog with its arguments. +

+ + + diff --git a/doc/quine-dam.txt b/doc/quine-dam.txt new file mode 100644 index 0000000..c69a52c --- /dev/null +++ b/doc/quine-dam.txt @@ -0,0 +1,110 @@ +#! /command/execlineb -P +# Public Domain. +# See comments below. +# (Search for "HERE".) +# +define -sCd "\n" lns " +${p} ${bubble} is the end of the quine's data. +${p} They represent the following code, with various quotations: +${p} ${b} (backslash) is represented as ${d}${ob}b${cb} +${p} ${q} (double quote) is represented as ${d}${ob}q${cb} +${p} ${p} (sharp/pound/shibboleth/whatever) is represented as ${d}${ob}p${cb} +${p} ${ob} (open brace) is represented as ${d}${ob}ob${cb} +${p} ${cb} (closed brace) is represented as ${d}${ob}cb${cb} +${p} ${d} (dollar) is represented as ${d}${ob}d${cb} +${p} ${bubble} (the magic word) is represented as ${d}${ob}bubble${cb} +${p} (The point of the magic word is to allow the reader +${p} to conveniently skip over the large data section.) +${p} +${p} Now we have the quine's code! +${p} +${p} First, print the lines that come before the data. +foreground ${ob} printf %s ${b}${p}${b}!${q} ${q} ${cb} +foreground ${ob} printf %s${b}${b}n ${q}/command/execlineb -P${q} ${cb} +foreground ${ob} printf %s${b}${b}n ${b}${p}${q} Public Domain.${q} ${cb} +foreground ${ob} printf %s${b}${b}n ${b}${p}${q} See comments below.${q} ${cb} +foreground ${ob} printf %s ${b}${p}${q} (Search for ${q} ${cb} +foreground ${ob} printf %s${b}${b}n ${b}${q}${bubble}${b}${q}.) ${cb} +foreground ${ob} printf %s${b}${b}n ${b}${p} ${cb} +foreground ${ob} printf %s ${q}define -sCd ${b}${q}${b}${b}n${b}${q} lns ${b}${q}${q} ${cb} +${p} Next, print the data themselves, as data. +for lin ${ob} ${d}${ob}lns${cb} ${cb} ${ob} +multisubstitute ${ob} +define b ${d}${ob}b${cb} +define q ${d}${ob}q${cb} +define p ${d}${ob}p${cb} +define ob ${d}${ob}ob${cb} +define cb ${d}${ob}cb${cb} +define d ${d}${ob}d${cb} +define bubble ${d}${ob}bubble${cb} +define intron ${d}${ob}intron${cb} +${cb} printf ${b}${b}n%s ${d}${ob}lin${cb} ${cb} +foreground ${ob} printf %s${b}${b}n ${b}${q} ${cb} +${p} Finally, use the data to print the code! +for lin ${ob} ${d}${ob}lns${cb} ${cb} ${ob} +multisubstitute ${ob} +define b ${b}${b} +define q ${b}${q} +define p ${b}${p} +define ob ${b}${ob} +define cb ${b}${cb} +define d ${d} +define bubble ${bubble} +define intron ${q}${intron}${q} +${cb} printf %s${b}${b}n ${d}${ob}lin${cb} ${cb} +${p} That's all, folks! - Well, that wasn't so hard, was it? +${p} (This quine was written by - see +${p} +${p} for more information on quines and how to write them.)" +# HERE is the end of the quine's data. +# They represent the following code, with various quotations: +# \ (backslash) is represented as ${b} +# " (double quote) is represented as ${q} +# # (sharp/pound/shibboleth/whatever) is represented as ${p} +# { (open brace) is represented as ${ob} +# } (closed brace) is represented as ${cb} +# $ (dollar) is represented as ${d} +# HERE (the magic word) is represented as ${bubble} +# (The point of the magic word is to allow the reader +# to conveniently skip over the large data section.) +# +# Now we have the quine's code! +# +# First, print the lines that come before the data. +foreground { printf %s \#\!" " } +foreground { printf %s\\n "/command/execlineb -P" } +foreground { printf %s\\n \#" Public Domain." } +foreground { printf %s\\n \#" See comments below." } +foreground { printf %s \#" (Search for " } +foreground { printf %s\\n \"HERE\".) } +foreground { printf %s\\n \# } +foreground { printf %s "define -sCd \"\\n\" lns \"" } +# Next, print the data themselves, as data. +for lin { ${lns} } { +multisubstitute { +define b ${b} +define q ${q} +define p ${p} +define ob ${ob} +define cb ${cb} +define d ${d} +define bubble ${bubble} +define intron ${intron} +} printf \\n%s ${lin} } +foreground { printf %s\\n \" } +# Finally, use the data to print the code! +for lin { ${lns} } { +multisubstitute { +define b \\ +define q \" +define p \# +define ob \{ +define cb \} +define d $ +define bubble HERE +define intron "NOTICE HOW THIS SENTENCE APPEARS ONLY ONCE IN THIS QUINE?" +} printf %s\\n ${lin} } +# That's all, folks! - Well, that wasn't so hard, was it? +# (This quine was written by - see +# +# for more information on quines and how to write them.) diff --git a/doc/quine-jriou.txt b/doc/quine-jriou.txt new file mode 100644 index 0000000..f8e5455 --- /dev/null +++ b/doc/quine-jriou.txt @@ -0,0 +1,28 @@ +#!/command/execlineb +define A "#!/command/execlineb" +define B "fine G $ foreground { echo ${C} } +echo -n foreground ${D} define C ${E}${C}${R}foreground +${D} echo ${G}${D}A${H} ${H}${R}foreground +${D} echo define A ${G}${D}C${H}${G}${D}A${H}${G}${D}C${H} ${H}${R}echo +-n define B ${G}${D}C${H} ${H}${R}foreground +${D} echo -n ${G}${D}B${H} ${H}${R}foreground +${D} multisubstitute ${D} define C ${E}${C} define D ${E}${D}${R}define +E ${E}${E}${R}define +H ${E}${H} define R ${C}${R}${C} ${H} de } echo ${B}" +foreground { define C \" +foreground { echo ${A} } +foreground { echo define A ${C}${A}${C} } +echo -n define B ${C} } +foreground { echo -n ${B} } +foreground { multisubstitute { define C \" define D \{ +define E \\ +define H \} define R " +" } define G $ foreground { echo ${C} } +echo -n foreground ${D} define C ${E}${C}${R}foreground +${D} echo ${G}${D}A${H} ${H}${R}foreground +${D} echo define A ${G}${D}C${H}${G}${D}A${H}${G}${D}C${H} ${H}${R}echo +-n define B ${G}${D}C${H} ${H}${R}foreground +${D} echo -n ${G}${D}B${H} ${H}${R}foreground +${D} multisubstitute ${D} define C ${E}${C} define D ${E}${D}${R}define +E ${E}${E}${R}define +H ${E}${H} define R ${C}${R}${C} ${H} de } echo ${B} diff --git a/doc/quine-prj-2.txt b/doc/quine-prj-2.txt new file mode 100644 index 0000000..9c60b92 --- /dev/null +++ b/doc/quine-prj-2.txt @@ -0,0 +1,15 @@ +#!/command/execlineb +define e "#!/command/execlineb +define e ${q}${E}${q} +multisubstitute { +define q ${b}${q} +define b ${b}${b} +define E $e +} +echo $e" +multisubstitute { +define q \" +define b \\ +define E $e +} +echo $e diff --git a/doc/quine-prj-3.txt b/doc/quine-prj-3.txt new file mode 100644 index 0000000..e5b5708 --- /dev/null +++ b/doc/quine-prj-3.txt @@ -0,0 +1,13 @@ +#!/command/execlineb -P +define e "#!/command/execlineb -P +define e ${q}${E}${q} +export E $e +define q ${b}${q} +define b ${b}${b} +import E +echo $e" +export E $e +define q \" +define b \\ +import E +echo $e diff --git a/doc/quine-prj.txt b/doc/quine-prj.txt new file mode 100644 index 0000000..8d2643f --- /dev/null +++ b/doc/quine-prj.txt @@ -0,0 +1,13 @@ +#!/command/execlineb +define e "#!/command/execlineb +define e $q${E}${q} +env e=$e +define q ${b}${q} +define b ${b}${b} +importas E e +echo $e" +env e=$e +define q \" +define b \\ +importas E e +echo $e diff --git a/doc/redirfd.html b/doc/redirfd.html new file mode 100644 index 0000000..fd29ef4 --- /dev/null +++ b/doc/redirfd.html @@ -0,0 +1,100 @@ + + + + + execline: the redirfd command + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The redirfd program

+ +

+redirfd redirects a given file descriptor to a file, then +executes a program. +

+ +

Interface

+ +
+     redirfd [ -r | -w | -u | -a | -c | -x ] [ -n | -b ] fd file prog...
+
+ +

+redirfd redirects the file descriptor number fd +to file, then execs into prog.... +

+ +

Options

+ +

+ One and only one of the -r, -w, -u, -a, -c, or -x options must be given; +the -n and -b options may be added in any case. +

+ +
    +
  • -r : open file for reading.
  • +
  • -w : open file for writing, truncating it if it already exists.
  • +
  • -u : open file for reading and writing.
  • +
  • -a : open file for appending, creating it if it doesn't exist.
  • +
  • -c : open file for appending. Do not create it if it doesn't exist.
  • +
  • -x : open file for writing, creating it, failing if it already exists.
  • +
  • -n : open file in non-blocking mode.
  • +
  • -b : change mode of file after opening it: +to non-blocking mode if the -n option was not given, +to blocking mode if it was.
  • +
+ +

Notes

+ +
    +
  • redirfd -r n file prog... is roughly equivalent to +sh -c 'exec prog... n<file'
  • +
  • redirfd -w n file prog... is roughly equivalent to +sh -c 'exec prog... n>file'
  • +
  • redirfd -u n file prog... is roughly equivalent to +sh -c 'exec prog... n<>file'
  • +
  • redirfd -a n file prog... is roughly equivalent to +sh -c 'exec prog... n>>file'
  • +
  • redirfd -c n file prog... has no portable +shell equivalent. Some shells provide the noclobber option for +a similar feature.
  • +
  • redirfd -x n file prog... has no portable +shell equivalent.
  • +
+ +

Special fifo handling

+ +

+ The -n and -b options are especially useful with +named pipes. +

+ +
    +
  • Opening a fifo for reading, blocking if there is no writer: +redirfd -r n fifo prog...
  • +
  • Opening a fifo for reading, with instant success even if +there is no writer, and blocking at the first attempt to read from it: +redirfd -r -nb n fifo prog...
  • +
  • Opening a fifo for writing, blocking if there is no reader: +redirfd -w n fifo prog...
  • +
  • Opening a fifo for writing, with instant success even if +there is no reader: +redirfd -w -nb n fifo prog.... Warning: +the first attempt to write to the fifo will raise a SIGPIPE if there is +still no reader at that time. The named pipe semantics normally do not +allow a fifo to be open for writing without a reading end, and you +should know what you are doing if you're using redirfd +this way.
  • +
+ + + diff --git a/doc/runblock.html b/doc/runblock.html new file mode 100644 index 0000000..6f5386e --- /dev/null +++ b/doc/runblock.html @@ -0,0 +1,75 @@ + + + + + execline: the runblock program + + + + + + +

+execline
+Software
+www.skarnet.org +

+ +

The runblock program

+ +

+runblock's purpose is to help you write execline commands +in the execline language. It can only be used inside an execline +script. If the script has been given blocks as arguments, runblock +allows you to execute one of the blocks individually. +

+ +

Interface

+ +
+     runblock [ -P ] [ -n argshift ] [ -r ] n
+
+ +
    +
  • runblock skips the first argshift positional +parameters. It does that to allow you to design commands that take simple +arguments and blocks.
  • +
  • Then runblock looks for and parses +blocks in the positional parameters.
  • +
  • If the -r option is present: runblock skips +n blocks and execs into the remaining arguments.
  • +
  • Else it skips n-1 blocks and execs into the nth +one.
  • +
  • Normally runblock pops +its environment frame before executing. If the -P option has +been given, it does not pop.
  • +
  • Of course, if the block structure doesn't match, runblock +exits 100 with an error message.
  • +
+ +

Example: implementing the ifelse command

+ +

+ Suppose that we want to implement the ifelse command as +an execline script, using the ifte command. +runblock allows us to do it in a simple way: +

+ +
+ #!/command/execlineb
+ ifte { runblock 2 } { runblock -r 2 } runblock 1
+
+ +

+ That's it. +

+ +

Credits

+ +

+ The runblock idea, as well as the ifelse idea, comes +from Paul Jarc. +

+ + + diff --git a/doc/shift.html b/doc/shift.html new file mode 100644 index 0000000..775e472 --- /dev/null +++ b/doc/shift.html @@ -0,0 +1,68 @@ + + + + + execline: the shift program + + + + + + +

+execline
+Software
+www.skarnet.org +

+ +

The shift program

+ +

+shift shifts the positional parameters of an execline script. +

+ +

Interface

+ +
+     shift [ -n argn ] [ -b blockn ] prog...
+
+ +
    +
  • shift shifts argn positional parameters, +then blockn blocks. It then execs prog....
  • +
  • By default, argn and blockn are both zero; +but if neither the -n nor the -b option is given, +then argn is 1 and blockn is 0.
  • +
+ +

Details

+ +
    +
  • shift reads the number of "positional parameters" in the +# environment variable. Let n be that number.
  • +
  • If the # environment variable is not set or does not +contain a valid number, or one of the 0, 1, ..., +n environment variables is not set, shift +exits 100 with an error message.
  • +
  • shift calculates a shift value m, corresponding +to argn arguments followed by enough arguments to make +blockn blocks.
  • +
  • It shifts the positional parameters m times: the +value of the m+1 variable becomes the value of the +1 variable, m+2 becomes 2 and so on, +and # is set to n-m (floored at zero).
  • +
  • shift then execs into prog....
  • +
+ +

Notes

+ +
    +
  • shift is a standard shell builtin. Be careful if you +want to use it outside of an execline script.
  • +
  • The -b option is only useful to implement execline +commands in the execline language. You shouldn't normally have to +use it.
  • +
+ + + diff --git a/doc/tryexec.html b/doc/tryexec.html new file mode 100644 index 0000000..2fcc7b6 --- /dev/null +++ b/doc/tryexec.html @@ -0,0 +1,67 @@ + + + + + execline: the tryexec command + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The tryexec program

+ +

+tryexec executes into a command line, with a fallback. +

+ +

Interface

+ +

+ In an execlineb script: +

+ +
+     tryexec [ -n ] [ -c ] [ -l ] [ -a argv0 ] { prog1... } prog2...
+
+ +
    +
  • tryexec reads prog1... in a +block. It then executes into it, +completely forgetting prog2...
  • +
  • If for some reason the execve() fails - for instance, +a non-executable prog1 - then tryexec executes +into prog2... instead.
  • +
+ +

Options

+ +
    +
  • -n : reverse prog1... and prog2...'s +role. The latter becomes the main execution path and the former becomes +the fallback.
  • +
+ +

+ The -c, -l and -a options have the same +semantics as with the exec program. +

+ +

Notes

+ +
    +
  • tryexec prog1... "" prog2... would be +equivalent to +sh -c 'exec prog1... || exec prog2...', if +such a shell construct existed. Unfortunately, the shell language does +not offer that functionality.
  • +
+ + + diff --git a/doc/umask.html b/doc/umask.html new file mode 100644 index 0000000..5edf511 --- /dev/null +++ b/doc/umask.html @@ -0,0 +1,44 @@ + + + + + execline: the umask command + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The umask program

+ +

+umask sets the umask (file creation mask), +then executes a program. +

+ +

Interface

+ +
+     umask mask prog...
+
+ +

+umask sets the current umask to mask, +then execs into prog.... +

+ +

Notes

+ +

+umask is a standard shell builtin. Be careful if you want to +use the umask command outside of an execline script. +

+ + + diff --git a/doc/unexport.html b/doc/unexport.html new file mode 100644 index 0000000..331189c --- /dev/null +++ b/doc/unexport.html @@ -0,0 +1,46 @@ + + + + + execline: the unexport command + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The unexport program

+ +

+unexport removes a variable from the environment, then +executes a program. +

+ +

Interface

+ +
+     unexport var prog...
+
+ +

+unexport removes the var variable from the +environment, then execs into prog with +its arguments. +

+ +

Notes

+ +
    +
  • Unsetting var is quite different from setting it to an +empty value. Shell scripts usually won't make the distinction; +execline does.
  • +
+ + + diff --git a/doc/upgrade.html b/doc/upgrade.html new file mode 100644 index 0000000..9cd795b --- /dev/null +++ b/doc/upgrade.html @@ -0,0 +1,35 @@ + + + + + execline: how to upgrade + + + + + + +

+execline
+Software
+skarnet.org +

+ + +

to 2.0.0.0

+ +
    +
  • The build system has completely changed. It is now a standard +./configure && make & & sudo make install +build system. See the enclosed INSTALL file for details.
  • +
  • slashpackage is not activated by default.
  • +
  • shared libraries are neither built nor used by default.
  • +
  • skalibs dependency bumped to 2.0.0.0
  • +
  • The obsolete -E option to backtick, forx and forbacktickx is not +supported anymore.
  • +
  • multisubstitute does not support the "backtick" directive +anymore.
  • +
+ + + diff --git a/doc/wait.html b/doc/wait.html new file mode 100644 index 0000000..680cd78 --- /dev/null +++ b/doc/wait.html @@ -0,0 +1,53 @@ + + + + + execline: the wait command + + + + + + +

+execline
+Software
+skarnet.org +

+ +

The wait program

+ +

+wait waits for a set of children, then executes a program. +

+ +

Interface

+ +

+ In an execlineb script: +

+ +
+     wait [ -r ] { [ pids... ] } prog...
+
+ +
    +
  • wait reads a list of pids in a +(possibly empty) block, +and unquotes it.
  • +
  • wait waits for every child whose pid is +listed in pids.... If pids... is an +empty list, it waits for every child process it has.
  • +
  • wait then execs into prog....
  • +
+ +

Options

+ +
    +
  • -r : reap mode. Do not pause until a child has +exited; only reap all pending zombies. The read block must be empty +for that option to be effective.
  • +
+ + + diff --git a/package/deps-build b/package/deps-build new file mode 100644 index 0000000..05d5af4 --- /dev/null +++ b/package/deps-build @@ -0,0 +1 @@ +/package/prog/skalibs diff --git a/package/deps.mak b/package/deps.mak new file mode 100644 index 0000000..96211e9 --- /dev/null +++ b/package/deps.mak @@ -0,0 +1,110 @@ +# +# This file has been generated by tools/gen-deps.sh +# + +src/execline/background.o src/execline/background.lo: src/execline/background.c src/include/execline/execline.h +src/execline/backtick.o src/execline/backtick.lo: src/execline/backtick.c src/include/execline/execline.h +src/execline/cd.o src/execline/cd.lo: src/execline/cd.c +src/execline/define.o src/execline/define.lo: src/execline/define.c src/include-local/exlsn.h +src/execline/dollarat.o src/execline/dollarat.lo: src/execline/dollarat.c +src/execline/elgetopt.o src/execline/elgetopt.lo: src/execline/elgetopt.c src/include/execline/execline.h +src/execline/elgetpositionals.o src/execline/elgetpositionals.lo: src/execline/elgetpositionals.c src/include-local/exlsn.h +src/execline/elglob.o src/execline/elglob.lo: src/execline/elglob.c src/include-local/exlsn.h +src/execline/emptyenv.o src/execline/emptyenv.lo: src/execline/emptyenv.c src/include/execline/execline.h +src/execline/exec.o src/execline/exec.lo: src/execline/exec.c +src/execline/execlineb.o src/execline/execlineb.lo: src/execline/execlineb.c src/include/execline/execline.h src/include-local/exlsn.h +src/execline/exit.o src/execline/exit.lo: src/execline/exit.c +src/execline/export.o src/execline/export.lo: src/execline/export.c +src/execline/fdblock.o src/execline/fdblock.lo: src/execline/fdblock.c +src/execline/fdclose.o src/execline/fdclose.lo: src/execline/fdclose.c +src/execline/fdmove.o src/execline/fdmove.lo: src/execline/fdmove.c +src/execline/fdreserve.o src/execline/fdreserve.lo: src/execline/fdreserve.c +src/execline/forbacktickx.o src/execline/forbacktickx.lo: src/execline/forbacktickx.c src/include/execline/config.h src/include/execline/execline.h +src/execline/foreground.o src/execline/foreground.lo: src/execline/foreground.c src/include/execline/execline.h +src/execline/forx.o src/execline/forx.lo: src/execline/forx.c src/include/execline/config.h src/include/execline/execline.h +src/execline/getpid.o src/execline/getpid.lo: src/execline/getpid.c +src/execline/heredoc.o src/execline/heredoc.lo: src/execline/heredoc.c +src/execline/homeof.o src/execline/homeof.lo: src/execline/homeof.c +src/execline/if.o src/execline/if.lo: src/execline/if.c src/include/execline/execline.h +src/execline/ifelse.o src/execline/ifelse.lo: src/execline/ifelse.c src/include/execline/execline.h +src/execline/ifte.o src/execline/ifte.lo: src/execline/ifte.c src/include/execline/execline.h +src/execline/ifthenelse.o src/execline/ifthenelse.lo: src/execline/ifthenelse.c src/include/execline/execline.h +src/execline/import.o src/execline/import.lo: src/execline/import.c src/include-local/exlsn.h +src/execline/importas.o src/execline/importas.lo: src/execline/importas.c src/include-local/exlsn.h +src/execline/loopwhilex.o src/execline/loopwhilex.lo: src/execline/loopwhilex.c src/include/execline/execline.h +src/execline/multidefine.o src/execline/multidefine.lo: src/execline/multidefine.c src/include-local/exlsn.h +src/execline/multisubstitute.o src/execline/multisubstitute.lo: src/execline/multisubstitute.c src/include/execline/execline.h src/include-local/exlsn.h +src/execline/pipeline.o src/execline/pipeline.lo: src/execline/pipeline.c src/include/execline/execline.h +src/execline/piperw.o src/execline/piperw.lo: src/execline/piperw.c +src/execline/redirfd.o src/execline/redirfd.lo: src/execline/redirfd.c +src/execline/runblock.o src/execline/runblock.lo: src/execline/runblock.c src/include/execline/execline.h +src/execline/shift.o src/execline/shift.lo: src/execline/shift.c src/include/execline/execline.h +src/execline/tryexec.o src/execline/tryexec.lo: src/execline/tryexec.c src/include/execline/execline.h +src/execline/umask.o src/execline/umask.lo: src/execline/umask.c +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_obsolescent.o src/libexecline/el_obsolescent.lo: src/libexecline/el_obsolescent.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 +src/libexecline/el_spawn0.o src/libexecline/el_spawn0.lo: src/libexecline/el_spawn0.c src/include/execline/execline.h +src/libexecline/el_spawn1.o src/libexecline/el_spawn1.lo: src/libexecline/el_spawn1.c src/include/execline/execline.h +src/libexecline/el_substandrun.o src/libexecline/el_substandrun.lo: src/libexecline/el_substandrun.c src/include-local/exlsn.h +src/libexecline/el_substandrun_str.o src/libexecline/el_substandrun_str.lo: src/libexecline/el_substandrun_str.c src/include/execline/execline.h src/include-local/exlsn.h +src/libexecline/el_substitute.o src/libexecline/el_substitute.lo: src/libexecline/el_substitute.c src/include/execline/execline.h +src/libexecline/el_transform.o src/libexecline/el_transform.lo: src/libexecline/el_transform.c src/include/execline/execline.h +src/libexecline/el_vardupl.o src/libexecline/el_vardupl.lo: src/libexecline/el_vardupl.c src/include/execline/execline.h +src/libexecline/exlp.o src/libexecline/exlp.lo: src/libexecline/exlp.c src/include/execline/execline.h src/include-local/exlsn.h +src/libexecline/exlsn_define.o src/libexecline/exlsn_define.lo: src/libexecline/exlsn_define.c src/include/execline/execline.h src/include-local/exlsn.h +src/libexecline/exlsn_elglob.o src/libexecline/exlsn_elglob.lo: src/libexecline/exlsn_elglob.c src/include/execline/execline.h src/include-local/exlsn.h +src/libexecline/exlsn_exlp.o src/libexecline/exlsn_exlp.lo: src/libexecline/exlsn_exlp.c src/include-local/exlsn.h +src/libexecline/exlsn_free.o src/libexecline/exlsn_free.lo: src/libexecline/exlsn_free.c src/include-local/exlsn.h +src/libexecline/exlsn_import.o src/libexecline/exlsn_import.lo: src/libexecline/exlsn_import.c src/include/execline/execline.h src/include-local/exlsn.h +src/libexecline/exlsn_main.o src/libexecline/exlsn_main.lo: src/libexecline/exlsn_main.c src/include/execline/execline.h src/include-local/exlsn.h +src/libexecline/exlsn_multidefine.o src/libexecline/exlsn_multidefine.lo: src/libexecline/exlsn_multidefine.c src/include/execline/execline.h src/include-local/exlsn.h + +background:src/execline/background.o -lexecline -lskarnet +backtick:src/execline/backtick.o -lexecline -lskarnet +cd:src/execline/cd.o -lskarnet +define:src/execline/define.o -lexecline -lskarnet +dollarat:src/execline/dollarat.o -lskarnet +elgetopt:src/execline/elgetopt.o -lexecline -lskarnet +elgetpositionals:src/execline/elgetpositionals.o -lexecline -lskarnet +elglob:src/execline/elglob.o -lexecline -lskarnet +emptyenv:src/execline/emptyenv.o -lexecline -lskarnet +exec:src/execline/exec.o -lskarnet +execlineb:src/execline/execlineb.o -lexecline -lskarnet +exit:src/execline/exit.o -lskarnet +export:src/execline/export.o -lskarnet +fdblock:src/execline/fdblock.o -lskarnet +fdclose:src/execline/fdclose.o -lskarnet +fdmove:src/execline/fdmove.o -lskarnet +fdreserve:src/execline/fdreserve.o -lskarnet +forbacktickx:src/execline/forbacktickx.o -lexecline -lskarnet +foreground:src/execline/foreground.o -lexecline -lskarnet +forx:src/execline/forx.o -lexecline -lskarnet +getpid:src/execline/getpid.o -lskarnet +heredoc:src/execline/heredoc.o -lskarnet +homeof:src/execline/homeof.o -lskarnet +if:src/execline/if.o -lexecline -lskarnet +ifelse:src/execline/ifelse.o -lexecline -lskarnet +ifte:src/execline/ifte.o -lexecline -lskarnet +ifthenelse:src/execline/ifthenelse.o -lexecline -lskarnet +import:src/execline/import.o -lexecline -lskarnet +importas:src/execline/importas.o -lexecline -lskarnet +loopwhilex:src/execline/loopwhilex.o -lexecline -lskarnet +multidefine:src/execline/multidefine.o -lexecline -lskarnet +multisubstitute:src/execline/multisubstitute.o -lexecline -lskarnet +pipeline:src/execline/pipeline.o -lexecline -lskarnet +piperw:src/execline/piperw.o -lskarnet +redirfd:src/execline/redirfd.o -lskarnet +runblock:src/execline/runblock.o -lexecline -lskarnet +shift:src/execline/shift.o -lexecline -lskarnet +tryexec:src/execline/tryexec.o -lexecline -lskarnet +umask:src/execline/umask.o -lskarnet +unexport:src/execline/unexport.o -lskarnet +wait:src/execline/wait.o -lexecline -lskarnet +libexecline.a: src/libexecline/el_execsequence.o src/libexecline/el_getstrict.o src/libexecline/el_obsolescent.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_obsolescent.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 new file mode 100644 index 0000000..03775df --- /dev/null +++ b/package/info @@ -0,0 +1,4 @@ +package=execline +version=2.0.0.0 +category=admin +package_macro_name=EXECLINE diff --git a/package/modes b/package/modes new file mode 100644 index 0000000..29e242a --- /dev/null +++ b/package/modes @@ -0,0 +1,41 @@ +background 0755 +backtick 0755 +cd 0755 +define 0755 +dollarat 0755 +elgetopt 0755 +elgetpositionals 0755 +elglob 0755 +multidefine 0755 +multisubstitute 0755 +emptyenv 0755 +exec 0755 +exit 0755 +execlineb 0755 +export 0755 +fdblock 0755 +fdclose 0755 +fdreserve 0755 +fdmove 0755 +forx 0755 +forbacktickx 0755 +foreground 0755 +getpid 0755 +heredoc 0755 +homeof 0755 +if 0755 +ifelse 0755 +ifte 0755 +ifthenelse 0755 +import 0755 +importas 0755 +loopwhilex 0755 +piperw 0755 +pipeline 0755 +redirfd 0755 +runblock 0755 +shift 0755 +tryexec 0755 +umask 0755 +unexport 0755 +wait 0755 diff --git a/package/targets.mak b/package/targets.mak new file mode 100644 index 0000000..9583a50 --- /dev/null +++ b/package/targets.mak @@ -0,0 +1,51 @@ +BIN_TARGETS = \ +background \ +backtick \ +cd \ +define \ +dollarat \ +elgetopt \ +elgetpositionals \ +elglob \ +emptyenv \ +exec \ +execlineb \ +exit \ +export \ +fdblock \ +fdclose \ +fdmove \ +fdreserve \ +forbacktickx \ +foreground \ +forx \ +getpid \ +heredoc \ +homeof \ +if \ +ifelse \ +ifte \ +ifthenelse \ +import \ +importas \ +loopwhilex \ +multidefine \ +multisubstitute \ +pipeline \ +piperw \ +redirfd \ +runblock \ +shift \ +tryexec \ +umask \ +unexport \ +wait + +SBIN_TARGETS = +LIBEXEC_TARGETS = + +SHARED_LIBS = \ +libexecline.so + +STATIC_LIBS = \ +libexecline.a diff --git a/patch-for-solaris b/patch-for-solaris new file mode 100755 index 0000000..02f2e3c --- /dev/null +++ b/patch-for-solaris @@ -0,0 +1,17 @@ +#!/usr/xpg4/bin/sh + +patchit () { + echo '#!/usr/xpg4/bin/sh' > $1.tmp + tail -n +2 $1 >> $1.tmp + mv -f $1.tmp $1 + chmod 755 $1 +} + +patchit ./configure +patchit ./tools/install.sh +patchit ./tools/gen-deps.sh + +echo 'SHELL := /usr/xpg4/bin/sh' > Makefile.tmp +echo >> Makefile.tmp +cat Makefile >> Makefile.tmp +mv -f Makefile.tmp Makefile diff --git a/src/execline/background.c b/src/execline/background.c new file mode 100644 index 0000000..d3e96ff --- /dev/null +++ b/src/execline/background.c @@ -0,0 +1,76 @@ +/* ISC license. */ + +#include +#include +#ifdef EXECLINE_OLD_VARNAMES +#include +#endif +#include +#include +#include +#include +#include +#include + +#define USAGE "background [ -d ] { command... }" + +int main (int argc, char const **argv, char const *const *envp) +{ + pid_t pid ; + int argc1 ; + int df = 0 ; + PROG = "background" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "d", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'd' : df = 1 ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + argc1 = el_semicolon(argv) ; + if (!argc1) strerr_dief1x(100, "empty block") ; + if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ; + if (argc1 + 1 == argc) df = 0 ; + argv[argc1] = 0 ; + + if (df) + { + pid = doublefork() ; + switch (pid) + { + case -1: strerr_diefu1sys(111, "doublefork") ; + case 0: + PROG = "background (grandchild)" ; + pathexec0_run(argv, envp) ; + strerr_dieexec(127, argv[0]) ; + } + } + else + { + pid = el_spawn0(argv[0], argv, envp) ; + if (!pid) strerr_diefu2sys(111, "spawn ", argv[0]) ; + } + if (argc1 + 1 == argc) return 0 ; + { +#ifdef EXECLINE_OLD_VARNAMES + char fmt[UINT64_FMT * 2 + 10] = "!=" ; +#else + char fmt[UINT64_FMT + 2] = "!=" ; +#endif + register unsigned int i = 2 ; + i += uint64_fmt(fmt+i, (uint64)pid) ; fmt[i++] = 0 ; +#ifdef EXECLINE_OLD_VARNAMES + byte_copy(fmt+i, 8, "LASTPID=") ; i += 8 ; + i += uint64_fmt(fmt+i, (uint64)pid) ; fmt[i++] = 0 ; +#endif + pathexec_r(argv + argc1 + 1, envp, env_len(envp), fmt, i) ; + } + strerr_dieexec(111, argv[argc1+1]) ; +} diff --git a/src/execline/backtick.c b/src/execline/backtick.c new file mode 100644 index 0000000..d0f74e4 --- /dev/null +++ b/src/execline/backtick.c @@ -0,0 +1,84 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "backtick [ -i ] [ -n ] var { prog... } remainder..." +#define dieusage() strerr_dieusage(100, USAGE) + +int main (int argc, char const **argv, char const *const *envp) +{ + subgetopt_t localopt = SUBGETOPT_ZERO ; + int argc1 ; + stralloc modif = STRALLOC_ZERO ; + int insist = 0, chomp = 0 ; + PROG = "backtick" ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "ein", &localopt) ; + if (opt < 0) break ; + switch (opt) + { + case 'i' : insist = 1 ; break ; + case 'n' : chomp = 1 ; break ; + case 'e' : break ; /* compat */ + default : dieusage() ; + } + } + argc -= localopt.ind ; argv += localopt.ind ; + + if (argc < 2) dieusage() ; + if (!*argv[0]) strerr_dief1x(100, "empty variable not accepted") ; + if (!stralloc_cats(&modif, argv[0]) || !stralloc_catb(&modif, "=", 1)) + strerr_diefu1sys(111, "stralloc_catb") ; + argc-- ; argv++ ; + argc1 = el_semicolon(argv) ; + if (!argc1) strerr_dief1x(100, "empty block") ; + if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ; + + { + int p[2] ; + pid_t pid ; + if (pipe(p) < 0) strerr_diefu1sys(111, "pipe") ; + pid = fork() ; + switch (pid) + { + case -1: strerr_diefu1sys(111, "fork") ; + case 0: + argv[argc1] = 0 ; + fd_close(p[0]) ; + PROG = "backtick (child)" ; + if (fd_move(1, p[1]) < 0) strerr_diefu1sys(111, "fd_move") ; + pathexec_run(argv[0], argv, envp) ; + strerr_dieexec(111, argv[0]) ; + } + fd_close(p[1]) ; + if (!slurp(&modif, p[0])) strerr_diefu1sys(111, "slurp") ; + fd_close(p[0]) ; + if (wait_pid(pid, &p[0]) < 0) strerr_diefu1sys(111, "wait_pid") ; + if (insist && wait_status(p[0])) + strerr_dief1x(wait_status(p[0]), "child process exited non-zero") ; + } + if (argc == argc1 - 1) return 0 ; + if (!stralloc_0(&modif)) strerr_diefu1sys(111, "stralloc_catb") ; + { + unsigned int reallen = str_len(modif.s) ; + if (reallen < modif.len - 1) + { + if (insist) + strerr_dief1x(1, "child process output contained a null character") ; + else + modif.len = reallen + 1 ; + } + if (chomp && (modif.s[modif.len - 2] == '\n')) + modif.s[--modif.len - 1] = 0 ; + } + pathexec_r(argv + argc1 + 1, envp, env_len(envp), modif.s, modif.len) ; + strerr_dieexec(111, argv[argc1 + 1]) ; +} diff --git a/src/execline/cd.c b/src/execline/cd.c new file mode 100644 index 0000000..c774ce5 --- /dev/null +++ b/src/execline/cd.c @@ -0,0 +1,17 @@ +/* ISC license. */ + +#include +#include +#include + +#define USAGE "cd path prog..." + +int main (int argc, char const *const *argv, char const *const *envp) +{ + PROG = "cd" ; + if (argc < 3) strerr_dieusage(100, USAGE) ; + if (chdir(argv[1]) == -1) + strerr_diefu2sys(111, "chdir to ", argv[1]) ; + pathexec_run(argv[2], argv+2, envp) ; + strerr_dieexec(111, argv[2]) ; +} diff --git a/src/execline/define.c b/src/execline/define.c new file mode 100644 index 0000000..f87c531 --- /dev/null +++ b/src/execline/define.c @@ -0,0 +1,12 @@ +/* ISC license. */ + +#include +#include "exlsn.h" + +#define USAGE "define [ -n ] [ -s ] [ -C | -c ] [ -d delim ] key value prog..." + +int main (int argc, char const **argv, char const *const *envp) +{ + PROG = "define" ; + exlsn_main(argc, argv, envp, &exlsn_define, USAGE) ; +} diff --git a/src/execline/deps-exe/background b/src/execline/deps-exe/background new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/background @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/backtick b/src/execline/deps-exe/backtick new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/backtick @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/cd b/src/execline/deps-exe/cd new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/cd @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/define b/src/execline/deps-exe/define new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/define @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/dollarat b/src/execline/deps-exe/dollarat new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/dollarat @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/elgetopt b/src/execline/deps-exe/elgetopt new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/elgetopt @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/elgetpositionals b/src/execline/deps-exe/elgetpositionals new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/elgetpositionals @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/elglob b/src/execline/deps-exe/elglob new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/elglob @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/emptyenv b/src/execline/deps-exe/emptyenv new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/emptyenv @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/exec b/src/execline/deps-exe/exec new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/exec @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/execlineb b/src/execline/deps-exe/execlineb new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/execlineb @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/exit b/src/execline/deps-exe/exit new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/exit @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/export b/src/execline/deps-exe/export new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/export @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/fdblock b/src/execline/deps-exe/fdblock new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/fdblock @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/fdclose b/src/execline/deps-exe/fdclose new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/fdclose @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/fdmove b/src/execline/deps-exe/fdmove new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/fdmove @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/fdreserve b/src/execline/deps-exe/fdreserve new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/fdreserve @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/forbacktickx b/src/execline/deps-exe/forbacktickx new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/forbacktickx @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/foreground b/src/execline/deps-exe/foreground new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/foreground @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/forx b/src/execline/deps-exe/forx new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/forx @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/getpid b/src/execline/deps-exe/getpid new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/getpid @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/heredoc b/src/execline/deps-exe/heredoc new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/heredoc @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/homeof b/src/execline/deps-exe/homeof new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/homeof @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/if b/src/execline/deps-exe/if new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/if @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/ifelse b/src/execline/deps-exe/ifelse new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/ifelse @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/ifte b/src/execline/deps-exe/ifte new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/ifte @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/ifthenelse b/src/execline/deps-exe/ifthenelse new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/ifthenelse @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/import b/src/execline/deps-exe/import new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/import @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/importas b/src/execline/deps-exe/importas new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/importas @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/loopwhilex b/src/execline/deps-exe/loopwhilex new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/loopwhilex @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/multidefine b/src/execline/deps-exe/multidefine new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/multidefine @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/multisubstitute b/src/execline/deps-exe/multisubstitute new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/multisubstitute @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/pipeline b/src/execline/deps-exe/pipeline new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/pipeline @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/piperw b/src/execline/deps-exe/piperw new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/piperw @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/redirfd b/src/execline/deps-exe/redirfd new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/redirfd @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/runblock b/src/execline/deps-exe/runblock new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/runblock @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/shift b/src/execline/deps-exe/shift new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/shift @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/tryexec b/src/execline/deps-exe/tryexec new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/tryexec @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/deps-exe/umask b/src/execline/deps-exe/umask new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/umask @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/unexport b/src/execline/deps-exe/unexport new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/execline/deps-exe/unexport @@ -0,0 +1 @@ +-lskarnet diff --git a/src/execline/deps-exe/wait b/src/execline/deps-exe/wait new file mode 100644 index 0000000..8c3ba3b --- /dev/null +++ b/src/execline/deps-exe/wait @@ -0,0 +1,2 @@ +-lexecline +-lskarnet diff --git a/src/execline/dollarat.c b/src/execline/dollarat.c new file mode 100644 index 0000000..66b5c61 --- /dev/null +++ b/src/execline/dollarat.c @@ -0,0 +1,63 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "dollarat [ -n ] [ -0 | -d delimchar ]" + +int main (int argc, char const *const *argv, char const *const *envp) +{ + unsigned int n, i = 0 ; + char const *x ; + char delim = '\n' ; + int zero = 0 ; + int nl = 1 ; + PROG = "dollarat" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "nd:0", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'n' : nl = 0 ; break ; + case 'd' : delim = *l.arg ; break ; + case '0' : zero = 1 ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (zero) delim = 0 ; + x = env_get2(envp, "#") ; + if (!x) strerr_dienotset(100, "#") ; + if (!uint0_scan(x, &n)) strerr_dieinvalid(100, "#") ; + for (; i < n ; i++) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, i+1)] = 0 ; + x = env_get2(envp, fmt) ; + if (!x) strerr_dienotset(100, fmt) ; + if (delim || zero) + { + if ((buffer_puts(buffer_1, x) < 0) + || (((i < n-1) || nl) && (buffer_put(buffer_1, &delim, 1) < 0))) + strerr_diefu1sys(111, "write to stdout") ; + } + else + { + unsigned int written = 0 ; + if (!netstring_put(buffer_1, x, str_len(x), &written)) + strerr_diefu1sys(111, "write a netstring to stdout") ; + } + } + if (!buffer_flush(buffer_1)) + strerr_diefu1sys(111, "write to stdout") ; + return 0 ; +} diff --git a/src/execline/elgetopt.c b/src/execline/elgetopt.c new file mode 100644 index 0000000..4dfd4d8 --- /dev/null +++ b/src/execline/elgetopt.c @@ -0,0 +1,69 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "elgetopt optstring prog..." + +int main (int argc, char const *const *argv, char const *const *envp) +{ + unsigned int n, nbak ; + unsigned int envlen = env_len(envp) ; + stralloc modif = STRALLOC_ZERO ; + char const *x = env_get2(envp, "#") ; + PROG = "elgetopt" ; + if (argc < 3) strerr_dieusage(100, USAGE) ; + if (!x) strerr_dienotset(100, "#") ; + if (!uint0_scan(x, &n)) strerr_dieinvalid(100, "#") ; + nbak = n++ ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + char const *args[n+1] ; + register unsigned int i = 0 ; + for ( ; i < n ; i++) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, i)] = 0 ; + args[i] = env_get2(envp, fmt) ; + if (!args[i]) strerr_dienotset(100, fmt) ; + } + args[n] = 0 ; + for (;;) + { + char hmpf[11] = "ELGETOPT_?" ; + register int opt = sgetopt_r(n, args, argv[1], &l) ; + if (opt == -1) break ; + if (opt == '?') return 1 ; + hmpf[9] = opt ; + if (!env_addmodif(&modif, hmpf, l.arg ? l.arg : "1")) goto err ; + } + n -= l.ind ; + for (i = 0 ; i < nbak ; i++) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, i+1)] = 0 ; + if (!env_addmodif(&modif, fmt, (i < n) ? args[l.ind + i] : 0)) goto err ; + } + } + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, n)] = 0 ; + if (!env_addmodif(&modif, "#", fmt)) goto err ; + } + { + char const *const list[1] = { "ELGETOPT_" } ; + char const *v[envlen] ; + if (el_pushenv(&satmp, envp, envlen, list, 1) < 0) goto err ; + if (!env_make(v, envlen, satmp.s, satmp.len)) goto err ; + pathexec_r(argv+2, v, envlen, modif.s, modif.len) ; + strerr_dieexec(111, argv[2]) ; + } +err: + strerr_diefu1sys(111, "update environment") ; +} diff --git a/src/execline/elgetpositionals.c b/src/execline/elgetpositionals.c new file mode 100644 index 0000000..3ce4b69 --- /dev/null +++ b/src/execline/elgetpositionals.c @@ -0,0 +1,12 @@ +/* ISC license. */ + +#include +#include "exlsn.h" + +#define USAGE "elgetpositionals [ -P num ] prog..." + +int main (int argc, char const **argv, char const *const *envp) +{ + PROG = "elgetpositionals" ; + exlsn_main(argc, argv, envp, &exlsn_exlp, USAGE) ; +} diff --git a/src/execline/elglob.c b/src/execline/elglob.c new file mode 100644 index 0000000..ffff74a --- /dev/null +++ b/src/execline/elglob.c @@ -0,0 +1,12 @@ +/* ISC license. */ + +#include +#include "exlsn.h" + +#define USAGE "elglob [ -v ] [ -w ] [ -s ] [ -m ] [ -e ] [ -0 ] key pattern prog..." + +int main (int argc, char const **argv, char const *const *envp) +{ + PROG = "elglob" ; + exlsn_main(argc, argv, envp, &exlsn_elglob, USAGE) ; +} diff --git a/src/execline/emptyenv.c b/src/execline/emptyenv.c new file mode 100644 index 0000000..cae8869 --- /dev/null +++ b/src/execline/emptyenv.c @@ -0,0 +1,92 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "emptyenv [ -p | -c | -o | -P ] prog..." + +static void cleanupenv (char const *const *argv, char const *const *envp) +{ + stralloc sa = STRALLOC_ZERO ; + if (!pathexec_env("!", 0) || !pathexec_env("?", 0)) goto err ; +#ifdef EXECLINE_OLD_VARNAMES + if (!pathexec_env("LASTPID", 0) || !pathexec_env("LASTEXITCODE", 0)) goto err ; +#endif + for (; *envp ; envp++) + { + char const *s = *envp ; + sa.len = 0 ; + if (!str_diffn(s, "ELGETOPT_", 9) + || !str_diffn(s, "EXECLINE_", 9) + || !str_diffn(s, "FD", 2) + || (s[0] == '#') + || ((s[0] >= '0') && (s[0] <= '9'))) + if (!stralloc_catb(&sa, s, str_chr(s, '=')) + || !stralloc_0(&sa) + || !pathexec_env(sa.s, 0)) + goto err ; + } + stralloc_free(&sa) ; + pathexec(argv) ; + strerr_dieexec(111, argv[0]) ; +err: + strerr_diefu1sys(111, "clean up environment") ; +} + +int main (int argc, char const *const *argv, char const *const *envp) +{ + int flagpath = 0, flagcleanup = 0, flagopt = 0, flagpos = 0 ; + PROG = "emptyenv" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "pcoP", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'p' : flagpath = 1 ; break ; + case 'c' : flagcleanup = 1 ; break ; + case 'o' : flagopt = 1 ; break ; + case 'P' : flagpos = 1 ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (!argc) strerr_dieusage(100, USAGE) ; + if (flagcleanup) cleanupenv(argv, envp) ; + else if (!flagopt && !flagpos) + { + char const *newenv[2] = { 0, 0 } ; + if (flagpath) + for (; *envp ; envp++) + if (!str_diffn(*envp, "PATH=", 5)) + { + newenv[0] = *envp ; + break ; + } + pathexec_run(argv[0], argv, newenv) ; + } + else + { + static char const *const list[12] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "#", "ELGETOPT_" } ; + stralloc sa = STRALLOC_ZERO ; + unsigned int envlen = env_len(envp) ; + int n = el_popenv(&sa, envp, envlen, flagpos ? list : list + 11, 11 * flagpos + flagopt) ; + if (n < 0) strerr_diefu1sys(111, "pop current execline environment") ; + { + char const *v[envlen - n + 1] ; + if (!env_make(v, envlen-n, sa.s, sa.len)) strerr_diefu1sys(111, "env_make") ; + v[envlen-n] = 0 ; + pathexec_run(argv[0], argv, v) ; + } + stralloc_free(&sa) ; + } + strerr_dieexec(111, argv[0]) ; +} diff --git a/src/execline/exec.c b/src/execline/exec.c new file mode 100644 index 0000000..2213ba4 --- /dev/null +++ b/src/execline/exec.c @@ -0,0 +1,48 @@ +/* ISC license. */ + +#include +#include +#include +#include + +#define USAGE "exec [ -c ] [ -l ] [ -a argv0 ] prog..." + +int main (int argc, char const **argv, char const *const *envp) +{ + static char const *const zero = 0 ; + char const *executable = 0 ; + char const *argv0 = 0 ; + int dash = 0 ; + PROG = "exec" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "cla:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'c' : envp = &zero ; break ; + case 'l' : dash = 1 ; break ; + case 'a' : argv0 = l.arg ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (!argc) strerr_dieusage(100, USAGE) ; + + executable = argv[0] ; + if (argv0) argv[0] = argv0 ; + if (dash) + { + register unsigned int n = str_len(argv[0]) ; + char dashed[n+2] ; + dashed[0] = '-' ; + byte_copy(dashed+1, n+1, argv[0]) ; + argv[0] = (char const *)dashed ; + pathexec_run(executable, argv, envp) ; + } + else pathexec_run(executable, argv, envp) ; + strerr_dieexec(111, executable) ; +} diff --git a/src/execline/execlineb.c b/src/execline/execlineb.c new file mode 100644 index 0000000..1b9e7ad --- /dev/null +++ b/src/execline/execlineb.c @@ -0,0 +1,327 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "exlsn.h" + +#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 ; + unsigned int n = argc > nmin ? argc : nmin ; + unsigned int i = 0 ; + + if (!genalloc_ready(elsubst_t, &info.data, 3 + n)) return -1 ; + if (!stralloc_ready(&info.vars, 6 + (n << 1))) goto err ; + stralloc_catb(&info.vars, "#\0" "0\0@", 6) ; + { + elsubst_t blah[3] ; + char fmt[UINT_FMT] ; + blah[0].var = 0 ; blah[0].value = 0 ; blah[0].n = 1 ; + if (!stralloc_catb(&info.values, fmt, uint_fmt(fmt, argc)) || !stralloc_0(&info.values)) goto err ; + blah[1].var = 2 ; blah[1].value = info.values.len ; blah[1].n = 1 ; + if (!stralloc_catb(&info.values, dollar0, str_len(dollar0) + 1)) goto err ; + blah[2].var = 4 ; blah[2].value = info.values.len ; blah[2].n = argc ; + genalloc_catb(elsubst_t, &info.data, blah, 3) ; + } + for (; i < n ; i++) + { + elsubst_t blah ; + char fmt[UINT_FMT] ; + blah.var = info.vars.len ; blah.value = info.values.len ; blah.n = 1 ; + if (!stralloc_catb(&info.vars, fmt, uint_fmt(fmt, i+1)) || !stralloc_0(&info.vars)) goto err ; + if (!stralloc_catb(&info.values, i < argc ? argv[i] : "", i < argc ? str_len(argv[i]) + 1 : 1)) goto err ; + genalloc_append(elsubst_t, &info.data, &blah) ; + } + { + stralloc dst = STRALLOC_ZERO ; + int r = el_substitute(&dst, sa->s, sa->len, info.vars.s, info.values.s, genalloc_s(elsubst_t, &info.data), genalloc_len(elsubst_t, &info.data)) ; + if (r < 0) goto err ; + exlsn_free(&info) ; + stralloc_free(sa) ; + *sa = dst ; + return r ; + } + + err: + exlsn_free(&info) ; + 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] ; + unsigned int flagpushenv = 2 ; + PROG = "execlineb" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "pPqwWc:S:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'p' : flagpushenv = 1 ; break ; + case 'P' : flagpushenv = 0 ; break ; + case 'q' : flagstrict = 0 ; break ; + case 'w' : flagstrict = 1 ; break ; + case 'W' : flagstrict = 2 ; break ; + case 'c' : string = (unsigned char *)l.arg ; break ; + case 'S' : + { + if (!uint0_scan(l.arg, &nmin)) strerr_dieusage(100, USAGE) ; + flagpushenv = 3 ; + break ; + } + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (string) next = &nextinstring ; + else + { + if (!argv[0]) strerr_dieusage(100, USAGE) ; + initbuffer(argv[0]) ; + dollar0 = argv[0] ; + argv++ ; argc-- ; + next = &nextinbuffer ; + } + + nc = lex(&sa, next) ; + switch (nc) + { + case -4: strerr_dief2x(100, "unmatched ", "}") ; + case -3: strerr_dief2x(100, "unmatched ", "{") ; + case -2: strerr_dief1x(100, "syntax error") ; + case -1: strerr_diefu1sys(111, "parse script") ; + case 0 : return 0 ; + } + + if (flagstrict >= 0) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, (unsigned int)flagstrict)] = 0 ; + if (!env_addmodif(&modif, "EXECLINE_STRICT", flagstrict ? fmt : 0)) goto errenv ; + } + + if (flagpushenv == 3) + { + flagpushenv = 0 ; + if (flagstrict && ((unsigned int)argc < nmin)) + { + char fmtn[UINT_FMT] ; + char fmta[UINT_FMT] ; + fmtn[uint_fmt(fmtn, nmin)] = 0 ; + fmta[uint_fmt(fmta, argc)] = 0 ; + if (flagstrict > 1) + strerr_dief4x(100, "too few arguments: expecting at least ", fmtn, " but got ", fmta) ; + else + strerr_warnw4x("too few arguments: expecting at least ", fmtn, " but got ", fmta) ; + } + nc = myexlp(&sa, argv, argc, nmin, dollar0) ; + if (nc < 0) strerr_diefu1sys(111, "substitute positional parameters") ; + } + else if (flagpushenv) + { + char fmt[UINT_FMT] ; + register unsigned int i = 0 ; + fmt[uint_fmt(fmt, argc)] = 0 ; + if (!env_addmodif(&modif, "#", fmt)) goto errenv ; + if (!env_addmodif(&modif, "0", dollar0)) goto errenv ; + for (; i < (unsigned int)argc ; i++) + { + fmt[uint_fmt(fmt, i+1)] = 0 ; + if (!env_addmodif(&modif, fmt, argv[i])) goto errenv ; + } + } + + { + char const *v[nc+1] ; + if (!env_make(v, nc, sa.s, sa.len)) strerr_diefu1sys(111, "make argv") ; + v[nc] = 0 ; + + if (flagpushenv > 1) + { + static char const *const list[11] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "#" } ; + unsigned int envlen = env_len(envp) ; + char const *w[envlen] ; + if (el_pushenv(&satmp, envp, envlen, list, 11) < 0) goto errenv ; + if (!env_make(w, envlen, satmp.s, satmp.len)) goto errenv ; + pathexec_r(v, w, envlen, modif.s, modif.len) ; + stralloc_free(&satmp) ; + } + else if (modif.len) + pathexec_r(v, envp, env_len(envp), modif.s, modif.len) ; + else + pathexec_run(v[0], v, envp) ; + } + stralloc_free(&modif) ; + strerr_dieexec(111, sa.s) ; +errenv: + strerr_diefu1sys(111, "update environment") ; +} diff --git a/src/execline/exit.c b/src/execline/exit.c new file mode 100644 index 0000000..d9c6c20 --- /dev/null +++ b/src/execline/exit.c @@ -0,0 +1,15 @@ +/* ISC license. */ + +#include +#include + +#define USAGE "exit [ exitcode ]" + +int main (int argc, char const *const *argv) +{ + unsigned int e ; + PROG = "exit" ; + if (argc < 2) return 0 ; + if (!uint0_scan(argv[1], &e)) strerr_dieusage(100, USAGE) ; + return (int)e ; +} diff --git a/src/execline/export.c b/src/execline/export.c new file mode 100644 index 0000000..e7a7bbe --- /dev/null +++ b/src/execline/export.c @@ -0,0 +1,27 @@ +/* ISC license. */ + +#include +#include +#include +#include + +#define USAGE "export variable value prog..." + +int main (int argc, char const *const *argv, char const *const *envp) +{ + unsigned int len1 ; + PROG = "export" ; + if (argc < 4) strerr_dieusage(100, USAGE) ; + len1 = str_len(argv[1]) ; + if (byte_chr(argv[1], len1, '=') < len1) + strerr_dief2x(100, "invalid variable name: ", argv[1]) ; + { + unsigned int len2 = str_len(argv[2]) ; + char fmt[len1 + len2 + 2] ; + byte_copy(fmt, len1, argv[1]) ; + fmt[len1] = '=' ; + byte_copy(fmt + len1 + 1, len2 + 1, argv[2]) ; + pathexec_r(argv+3, envp, env_len(envp), fmt, len1 + len2 + 2) ; + } + strerr_dieexec(111, argv[3]) ; +} diff --git a/src/execline/fdblock.c b/src/execline/fdblock.c new file mode 100644 index 0000000..486591e --- /dev/null +++ b/src/execline/fdblock.c @@ -0,0 +1,34 @@ +/* ISC license. */ + +#include +#include +#include +#include + +#define USAGE "fdblock [ -n ] fd prog..." + +int main (int argc, char const *const *argv, char const *const *envp) +{ + unsigned int fd ; + int block = 1 ; + PROG = "fdblock" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "n", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'n' : block = 0 ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if ((argc < 2) || !uint0_scan(argv[0], &fd)) strerr_dieusage(100, USAGE) ; + if ((block ? ndelay_off(fd) : ndelay_on(fd)) < 0) + strerr_diefu1sys(111, block ? "ndelay_off" : "ndelay_on") ; + pathexec_run(argv[1], argv+1, envp) ; + strerr_dieexec(111, argv[1]) ; +} diff --git a/src/execline/fdclose.c b/src/execline/fdclose.c new file mode 100644 index 0000000..33b5941 --- /dev/null +++ b/src/execline/fdclose.c @@ -0,0 +1,17 @@ +/* ISC license. */ + +#include +#include +#include + +#define USAGE "fdclose fd prog..." + +int main (int argc, char const *const *argv, char const *const *envp) +{ + unsigned int fd ; + PROG = "fdclose" ; + if ((argc < 3) || !uint0_scan(argv[1], &fd)) strerr_dieusage(100, USAGE) ; + fd_close((int)fd) ; + pathexec_run(argv[2], argv+2, envp) ; + strerr_dieexec(111, argv[2]) ; +} diff --git a/src/execline/fdmove.c b/src/execline/fdmove.c new file mode 100644 index 0000000..8b7417e --- /dev/null +++ b/src/execline/fdmove.c @@ -0,0 +1,35 @@ +/* ISC license. */ + +#include +#include +#include +#include + +#define USAGE "fdmove [ -c ] to from prog..." + +int main (int argc, char const *const *argv, char const *const *envp) +{ + unsigned int to, from ; + int flagcopy = 0 ; + PROG = "fdmove" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "c", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'c' : flagcopy = 1 ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if ((argc < 3) || !uint0_scan(argv[0], &to) || !uint0_scan(argv[1], &from)) + strerr_dieusage(100, USAGE) ; + if ((flagcopy ? fd_copy((int)to, (int)from) : fd_move((int)to, (int)from)) == -1) + strerr_diefu4sys(111, "move fd ", argv[1], " to fd ", argv[0]) ; + pathexec_run(argv[2], argv+2, envp) ; + strerr_dieexec(111, argv[2]) ; +} diff --git a/src/execline/fdreserve.c b/src/execline/fdreserve.c new file mode 100644 index 0000000..2df721a --- /dev/null +++ b/src/execline/fdreserve.c @@ -0,0 +1,64 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include + +#define USAGE "fdreserve n prog..." + +#define MAXFDS 1024 + +unsigned int doit (char *modif, unsigned int i, int fd) +{ + register unsigned int pos = 2 ; + modif[0] = 'F' ; modif[1] = 'D' ; + pos += uint_fmt(modif + pos, i) ; + modif[pos++] = '=' ; + pos += uint_fmt(modif + pos, (unsigned int)fd) ; + modif[pos++] = 0 ; + return pos ; +} + +int main (int argc, char const *const *argv, char const *const *envp) +{ + unsigned int n ; + PROG = "fdreserve" ; + if ((argc < 3) || !uint0_scan(argv[1], &n)) + strerr_dieusage(100, USAGE) ; + { + struct rlimit lim ; + if (getrlimit(RLIMIT_NOFILE, &lim) < 0) strerr_diefu1sys(111, "getrlimit") ; + if (n > lim.rlim_cur) strerr_dief1x(100, "too many requested fds") ; + } + { + char modif[12 * n] ; /* enough for n times "FDaaaa=bbbb\0" */ + unsigned int j = 0 ; + { + int fd[n >> 1][2] ; + register unsigned int i = 0 ; + for (; i < (n>>1) ; i++) + if (pipe(fd[i]) < 0) + strerr_diefu1sys(111, "reserve fds") ; + if (n & 1) + { + register int lastfd = open_read("/dev/null") ; + if (lastfd < 0) + strerr_diefu1sys(111, "reserve last fd") ; + fd_close(lastfd) ; + j += doit(modif + j, n-1, lastfd) ; + } + for (i = 0 ; i < (n>>1) ; i++) + { + fd_close(fd[i][0]) ; + fd_close(fd[i][1]) ; + j += doit(modif + j, i<<1, fd[i][0]) ; + j += doit(modif + j, (i<<1)|1, fd[i][1]) ; + } + } + pathexec_r(argv+2, envp, env_len(envp), modif, j) ; + } + strerr_dieexec(111, argv[2]) ; +} diff --git a/src/execline/forbacktickx.c b/src/execline/forbacktickx.c new file mode 100644 index 0000000..f7b1460 --- /dev/null +++ b/src/execline/forbacktickx.c @@ -0,0 +1,145 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "forbacktickx [ -p | -x breakcode,breakcode,... ] [ -n ] [ -C | -c ] [ -0 | -d delim ] var { backtickcmd... } command..." +#define dieusage() strerr_dieusage(100, USAGE) + +static int isbreak (unsigned short *tab, unsigned int n, int code) +{ + register unsigned int i = 0 ; + for (; i < n ; i++) if ((unsigned short)code == tab[i]) break ; + return i < n ; +} + +int main (int argc, char const **argv, char const *const *envp) +{ + genalloc pids = GENALLOC_ZERO ; /* pid_t */ + char const *delim = " \n\r\t" ; + unsigned int delimlen = 4 ; + char const *x ; + int argc1 ; + unsigned short breakcodes[256] ; + unsigned int nbc = 0 ; + int crunch = 0, chomp = 0 ; + PROG = "forbacktickx" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "epnCc0d:x:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'e' : break ; /* compat */ + case 'p' : + { + if (!genalloc_ready(pid_t, &pids, 2)) + strerr_diefu1sys(111, "genalloc_ready") ; + break ; + } + case 'n' : chomp = 1 ; break ; + case 'C' : crunch = 1 ; break ; + case 'c' : crunch = 0 ; break ; + case '0' : delim = "" ; delimlen = 1 ; break ; + case 'd' : delim = l.arg ; delimlen = str_len(delim) ; break ; + case 'x' : + if (!ushort_scanlist(breakcodes, 256, l.arg, &nbc)) dieusage() ; + break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (argc < 2) dieusage() ; + x = argv[0] ; if (!*x) dieusage() ; + argv++ ; argc-- ; + argc1 = el_semicolon(argv) ; + if (!argc1) strerr_dief1x(100, "empty block") ; + if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ; + argv[argc1] = 0 ; + { + int fd ; + pid_t pidw = el_spawn1(argv[0], argv, envp, &fd, 1) ; + if (!pidw) strerr_diefu2sys(111, "spawn ", argv[0]) ; + { + char buf[BUFFER_INSIZE] ; + buffer b = BUFFER_INIT(&buffer_read, fd, buf, BUFFER_INSIZE) ; + stralloc modif = STRALLOC_ZERO ; + unsigned int envlen = env_len(envp) ; + unsigned int modifstart = str_len(x)+1 ; + char const *newenv[envlen + 2] ; + if (!stralloc_ready(&modif, modifstart+1)) + strerr_diefu1sys(111, "stralloc_ready") ; + byte_copy(modif.s, modifstart-1, x) ; + modif.s[modifstart-1] = '=' ; + for (;;) + { + pid_t pid ; + modif.len = modifstart ; + if (delimlen) + { + register int r = skagetlnsep(&b, &modif, delim, delimlen) ; + if (!r) break ; + else if (r < 0) + { + if (errno != EPIPE) strerr_diefu1sys(111, "skagetlnsep") ; + if (chomp) break ; + } + else modif.len-- ; + if ((modif.len == modifstart) && crunch) continue ; + } + else + { + unsigned int unread = 0 ; + if (netstring_get(&b, &modif, &unread) <= 0) + { + if (netstring_okeof(&b, unread)) break ; + else strerr_diefu1sys(111, "netstring_get") ; + } + } + if (!stralloc_0(&modif)) strerr_diefu1sys(111, "stralloc_0") ; + if (!env_merge(newenv, envlen+2, envp, envlen, modif.s, modif.len)) + strerr_diefu1sys(111, "merge environment") ; + pid = el_spawn0(argv[argc1 + 1], argv + argc1 + 1, newenv) ; + if (!pid) strerr_diefu2sys(111, "spawn ", argv[argc1+1]) ; + if (pids.s) + { + if (!genalloc_append(pid_t, &pids, &pid)) + strerr_diefu1sys(111, "genalloc_append") ; + } + else + { + int wstat ; + if (wait_pid(pid, &wstat) < 0) + strerr_diefu2sys(111, "wait for ", argv[argc1 + 1]) ; + if (isbreak(breakcodes, nbc, wait_status(wstat))) + return wait_status(wstat) ; + } + } + stralloc_free(&modif) ; + } + fd_close(fd) ; + if (!genalloc_append(pid_t, &pids, &pidw)) + strerr_diefu1sys(111, "genalloc_append") ; + } + if (!waitn(genalloc_s(pid_t, &pids), genalloc_len(pid_t, &pids))) + strerr_diefu1sys(111, "waitn") ; + /* genalloc_free(pid_t, &pids) ; */ + return 0 ; +} diff --git a/src/execline/foreground.c b/src/execline/foreground.c new file mode 100644 index 0000000..8164dcb --- /dev/null +++ b/src/execline/foreground.c @@ -0,0 +1,14 @@ +/* ISC license. */ + +#include +#include + +int main (int argc, char const **argv, char const *const *envp) +{ + int argc1 ; + PROG = "foreground" ; + argc1 = el_semicolon(++argv) ; + if (argc1 >= --argc) strerr_dief1x(100, "unterminated block") ; + argv[argc1] = 0 ; + el_execsequence(argv, argv+argc1+1, envp) ; +} diff --git a/src/execline/forx.c b/src/execline/forx.c new file mode 100644 index 0000000..25d6d44 --- /dev/null +++ b/src/execline/forx.c @@ -0,0 +1,91 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "forx [ -p | -x breakcode,breakcode,... ] var { values... } command..." +#define dieusage() strerr_dieusage(100, USAGE) + +static int isbreak (unsigned short *tab, unsigned int n, int code) +{ + register unsigned int i = 0 ; + for (; i < n ; i++) if ((unsigned short)code == tab[i]) break ; + return i < n ; +} + +int main (int argc, char const **argv, char const *const *envp) +{ + char const *x ; + int argc1 ; + unsigned short breakcodes[256] ; + unsigned int nbc = 0 ; + int flagpar = 0 ; + PROG = "forx" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "epx:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'e' : break ; /* compat */ + case 'p' : flagpar = 1 ; break ; + case 'x' : + if (!ushort_scanlist(breakcodes, 256, l.arg, &nbc)) dieusage() ; + break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + + if (argc < 2) dieusage() ; + x = argv[0] ; if (!*x) dieusage() ; + argv++ ; argc-- ; + argc1 = el_semicolon(argv) ; + if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ; + if (!argc1 || (argc1 + 1 == argc)) return 0 ; + { + unsigned int envlen = env_len(envp) ; + unsigned int varlen = str_len(x) ; + unsigned int i = 0 ; + pid_t pids[flagpar ? argc1 : 1] ; + char const *newenv[envlen + 2] ; + + for (; i < (unsigned int)argc1 ; i++) + { + pid_t pid ; + unsigned int vallen = str_len(argv[i]) ; + char modif[varlen + vallen + 2] ; + byte_copy(modif, varlen, x) ; + modif[varlen] = '=' ; + byte_copy(modif + varlen + 1, vallen, argv[i]) ; + modif[varlen + vallen + 1] = 0 ; + if (!env_merge(newenv, envlen + 2, envp, envlen, modif, varlen + vallen + 2)) + strerr_diefu1sys(111, "build new environment") ; + pid = el_spawn0(argv[argc1+1], argv + argc1 + 1, newenv) ; + if (!pid) strerr_diefu2sys(111, "spawn ", argv[argc1+1]) ; + if (flagpar) pids[i] = pid ; + else + { + int wstat ; + if (wait_pid(pid, &wstat) == -1) + strerr_diefu2sys(111, "wait for ", argv[argc1+1]) ; + if (isbreak(breakcodes, nbc, wait_status(wstat))) + return wait_status(wstat) ; + } + } + if (flagpar) + if (!waitn(pids, argc1)) strerr_diefu1sys(111, "waitn") ; + } + return 0 ; +} diff --git a/src/execline/getpid.c b/src/execline/getpid.c new file mode 100644 index 0000000..03a517c --- /dev/null +++ b/src/execline/getpid.c @@ -0,0 +1,29 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include + +#define USAGE "getpid variable prog..." + +int main (int argc, char const *const *argv, char const *const *envp) +{ + unsigned int len ; + PROG = "getpid" ; + if (argc < 3) strerr_dieusage(100, USAGE) ; + len = str_len(argv[1]) ; + if (byte_chr(argv[1], len, '=') < len) + strerr_dief2x(100, "invalid variable name: ", argv[1]) ; + { + char fmt[UINT_FMT + len + 2] ; + unsigned int i = len+1 ; + byte_copy(fmt, len, argv[1]) ; + fmt[len] = '=' ; + i += uint_fmt(fmt+i, getpid()) ; fmt[i++] = 0 ; + pathexec_r(argv+2, envp, env_len(envp), fmt, i) ; + } + strerr_dieexec(111, argv[2]) ; +} diff --git a/src/execline/heredoc.c b/src/execline/heredoc.c new file mode 100644 index 0000000..899c2d8 --- /dev/null +++ b/src/execline/heredoc.c @@ -0,0 +1,59 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "heredoc [ -d ] fd string command..." +#define dieusage() strerr_dieusage(100, USAGE) + +int main (int argc, char const *const *argv, char const *const *envp) +{ + int df = 0 ; + PROG = "heredoc" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "d", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'd' : df = 1 ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (argc < 3) dieusage() ; + { + int fd[2] ; + unsigned int fdr ; + int pid ; + if (!uint0_scan(argv[0], &fdr)) strerr_dieusage(100, USAGE) ; + if (pipe(fd) < 0) strerr_diefu1sys(111, "pipe") ; + pid = df ? doublefork() : fork() ; + switch (pid) + { + case -1: strerr_diefu2sys(111, df ? "double" : "", "fork") ; + case 0: + { + unsigned int len = str_len(argv[1]) ; + PROG = "heredoc (child)" ; + fd_close(fd[0]) ; + if (allwrite(fd[1], argv[1], len) < len) + strerr_diefu1sys(111, "allwrite") ; + return 0 ; + } + } + fd_close(fd[1]) ; + if (fd_move((int)fdr, fd[0]) == -1) + strerr_diefu2sys(111, "read on fd ", argv[0]) ; + } + pathexec_run(argv[2], argv+2, envp) ; + strerr_dieexec(111, argv[2]) ; +} diff --git a/src/execline/homeof.c b/src/execline/homeof.c new file mode 100644 index 0000000..40463da --- /dev/null +++ b/src/execline/homeof.c @@ -0,0 +1,28 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include + +#define USAGE "homeof user" + +int main (int argc, char const *const *argv) +{ + struct passwd *pw ; + PROG = "homeof" ; + if (argc < 2) strerr_dieusage(100, USAGE) ; + pw = getpwnam(argv[1]) ; + if (!pw) + { + if (errno) + strerr_diefu2sys(111, "get passwd entry for ", argv[1]) ; + else + strerr_diefu3x(111, "get passwd entry for ", argv[1], ": no such user") ; + } + if ((buffer_puts(buffer_1small, pw->pw_dir) < 0) + || (buffer_putflush(buffer_1small, "\n", 1) < 0)) + strerr_diefu1sys(111, "write to stdout") ; + return 0 ; +} diff --git a/src/execline/if.c b/src/execline/if.c new file mode 100644 index 0000000..9d0b4b4 --- /dev/null +++ b/src/execline/if.c @@ -0,0 +1,54 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "if [ -n ] [ -X ] [ -t | -x exitcode ] { command... }" + +int main (int argc, char const **argv, char const *const *envp) +{ + int argc1, wstat ; + pid_t pid ; + unsigned int not = 0 ; + unsigned short e = 1 ; + int flagnormalcrash = 0 ; + PROG = "if" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "nXtx:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'n' : not = 1 ; break ; + case 'X' : flagnormalcrash = 1 ; break ; + case 't' : e = 0 ; break ; + case 'x' : if (ushort_scan(l.arg, &e)) break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + argc1 = el_semicolon(argv) ; + if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ; + argv[argc1] = 0 ; + pid = el_spawn0(argv[0], argv, envp) ; + if (!pid) strerr_diefu2sys(111, "spawn ", argv[0]) ; + if (wait_pid(pid, &wstat) == -1) strerr_diefu1sys(111, "wait_pid") ; + if (!flagnormalcrash && WIFSIGNALED(wstat)) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, WTERMSIG(wstat))] = 0 ; + strerr_dief2x(1, "child crashed with signal ", fmt) ; + } + if (not == !wait_status(wstat)) return (int)e ; + pathexec0_run(argv+argc1+1, envp) ; + strerr_dieexec(111, argv[argc1+1]) ; +} diff --git a/src/execline/ifelse.c b/src/execline/ifelse.c new file mode 100644 index 0000000..6d8801e --- /dev/null +++ b/src/execline/ifelse.c @@ -0,0 +1,54 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "ifelse [ -n ] [ -X ] { command-if } { command-then... }" + +int main (int argc, char const **argv, char const *const *envp) +{ + int argc1, argc2, wstat ; + int not = 0, flagnormalcrash = 0 ; + pid_t pid ; + PROG = "ifelse" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "nX", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'n' : not = 1 ; break ; + case 'X' : flagnormalcrash = 1 ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + argc1 = el_semicolon(argv) ; + if (argc1 >= argc) strerr_dief1x(100, "unterminated if block") ; + if (argc1 + 1 == argc) strerr_dief1x(100, "then block required") ; + argc2 = el_semicolon(argv + argc1 + 1) ; + if (argc1 + argc2 + 1 >= argc) strerr_dief1x(100, "unterminated then block") ; + argv[argc1] = 0 ; + pid = el_spawn0(argv[0], argv, envp) ; + if (!pid) strerr_diefu2sys(111, "spawn ", argv[0]) ; + if (wait_pid(pid, &wstat) == -1) + strerr_diefu2sys(111, "wait for ", argv[0]) ; + argv += ++argc1 ; + if (!flagnormalcrash && WIFSIGNALED(wstat)) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, WSTOPSIG(wstat))] = 0 ; + strerr_dief2x(1, "child crashed with signal ", fmt) ; + } + if (not != !wait_status(wstat)) argv[argc2] = 0 ; else argv += argc2+1 ; + pathexec0_run(argv, envp) ; + strerr_dieexec(111, *argv) ; +} diff --git a/src/execline/ifte.c b/src/execline/ifte.c new file mode 100644 index 0000000..3fe021e --- /dev/null +++ b/src/execline/ifte.c @@ -0,0 +1,59 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "ifte [ -X ] [ -n ] { command-then... } { command-else... } command-if..." + +int main (int argc, char const **argv, char const *const *envp) +{ + int argc1, argc2, wstat ; + int not = 0, flagnormalcrash = 0 ; + pid_t pid ; + PROG = "ifte" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "Xn", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'X' : flagnormalcrash = 1 ; break ; + case 'n' : not = 1 ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + argc1 = el_semicolon(argv) ; + if (argc1 >= argc) strerr_dief1x(100, "unterminated then block") ; + if (argc1 + 1 == argc) strerr_dief1x(100, "else block required") ; + argc2 = el_semicolon(argv + argc1 + 1) ; + if (argc1 + argc2 + 1 >= argc) strerr_dief1x(100, "unterminated else block") ; + if (argc1 + argc2 + 2 >= argc) strerr_dief1x(100, "empty command-if") ; + + pid = el_spawn0(argv[argc1 + argc2 + 2], argv + argc1 + argc2 + 2, envp) ; + if (!pid) strerr_diefu2sys(111, "spawn ", argv[argc1 + argc2 + 2]) ; + if (wait_pid(pid, &wstat) == -1) + strerr_diefu2sys(111, "wait for ", argv[argc1 + argc2 + 2]) ; + if (!flagnormalcrash && WIFSIGNALED(wstat)) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, WTERMSIG(wstat))] = 0 ; + strerr_dief2x(1, "child crashed with signal ", fmt) ; + } + if (not != !wait_status(wstat)) argv[argc1] = 0 ; + else + { + argv += argc1 + 1 ; + argv[argc2] = 0 ; + } + pathexec0_run(argv, envp) ; + strerr_dieexec(111, *argv) ; +} diff --git a/src/execline/ifthenelse.c b/src/execline/ifthenelse.c new file mode 100644 index 0000000..f9fb9d7 --- /dev/null +++ b/src/execline/ifthenelse.c @@ -0,0 +1,77 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "ifthenelse [ -X ] { command-if... } { command-then... } { command-else... } [ remainder... ]" + +int main (int argc, char const **argv, char const *const *envp) +{ + int argc1, argc2, argc3, wstat ; + int magicscope = 0, flagnormalcrash = 0 ; + pid_t pid ; + PROG = "ifthenelse" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "Xs", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'X' : flagnormalcrash = 1 ; break ; + case 's' : magicscope = 1 ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + argc1 = el_semicolon(argv) ; + if (argc1 >= argc) strerr_dief1x(100, "unterminated if block") ; + if (argc1 + 1 == argc) strerr_dief1x(100, "then block required") ; + argc2 = el_semicolon(argv + argc1 + 1) ; + if (argc1 + argc2 + 1 >= argc) strerr_dief1x(100, "unterminated then block") ; + argc3 = el_semicolon(argv + argc1 + argc2 + 2) ; + if (argc1 + argc2 + argc3 + 2 >= argc) + strerr_dief1x(100, "unterminated else block") ; + + argv[argc1] = 0 ; + pid = fork() ; + pid = el_spawn0(argv[0], argv, envp) ; + if (!pid) strerr_diefu2sys(111, "spawn ", argv[0]) ; + if (wait_pid(pid, &wstat) == -1) + strerr_diefu2sys(111, "wait for ", argv[0]) ; + argv += argc1 + 1 ; + { + char const *const *remainder = argv + argc2 + argc3 + 2 ; + if (!flagnormalcrash && WIFSIGNALED(wstat)) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, WTERMSIG(wstat))] = 0 ; + strerr_dief2x(1, "child crashed with signal ", fmt) ; + } + if (wait_status(wstat)) + { + argv += argc2 + 1 ; + argc2 = argc3 ; + } + if (magicscope) /* undocumented voodoo - dangerous and powerful */ + { + register unsigned int i = 0 ; + for (; remainder[i] ; i++) argv[argc2+i] = remainder[i] ; + argv[argc2+i] = 0 ; + pathexec0_run(argv, envp) ; + strerr_dieexec(111, argv[0]) ; + } + else + { + argv[argc2] = 0 ; + el_execsequence(argv, remainder, envp) ; + } + } +} diff --git a/src/execline/import.c b/src/execline/import.c new file mode 100644 index 0000000..5a95886 --- /dev/null +++ b/src/execline/import.c @@ -0,0 +1,12 @@ +/* ISC license. */ + +#include +#include "exlsn.h" + +#define USAGE "import [ -i | -D default ] [ -n ] [ -s ] [ -C | -c ] [ -d delim ] var prog..." + +int main (int argc, char const **argv, char const *const *envp) +{ + PROG = "import" ; + exlsn_main(argc, argv, envp, &exlsn_import, USAGE) ; +} diff --git a/src/execline/importas.c b/src/execline/importas.c new file mode 100644 index 0000000..026efce --- /dev/null +++ b/src/execline/importas.c @@ -0,0 +1,12 @@ +/* ISC license. */ + +#include +#include "exlsn.h" + +#define USAGE "importas [ -i | -D default ] [ -n ] [ -s ] [ -C | -c ] [ -d delim ] key var prog..." + +int main (int argc, char const **argv, char const *const *envp) +{ + PROG = "importas" ; + exlsn_main(argc, argv, envp, &exlsn_importas, USAGE) ; +} diff --git a/src/execline/loopwhilex.c b/src/execline/loopwhilex.c new file mode 100644 index 0000000..5cb6a4e --- /dev/null +++ b/src/execline/loopwhilex.c @@ -0,0 +1,62 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "loopwhilex [ -n ] [ -x exitcode,exitcode,... ] prog..." +#define dieusage() strerr_dieusage(100, USAGE) + +static int isbreak (unsigned short *tab, unsigned int n, int code) +{ + register unsigned int i = 0 ; + for (; i < n ; i++) if ((unsigned short)code == tab[i]) break ; + return i < n ; +} + +int main (int argc, char const *const *argv, char const *const *envp) +{ + int wstat ; + int not = 0, cont = 1 ; + unsigned short breakcodes[256] ; + unsigned int nbc = 0 ; + PROG = "loopwhilex" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "nx:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'n' : not = 1 ; break ; + case 'x' : + if (!ushort_scanlist(breakcodes, 256, l.arg, &nbc)) dieusage() ; + break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (!argc) dieusage() ; + + if (!nbc) + { + breakcodes[0] = 0 ; + nbc = 1 ; + not = !not ; + } + + while (cont) + { + pid_t pid = el_spawn0(argv[0], argv, envp) ; + if (!pid) strerr_diefu2sys(111, "spawn ", argv[0]) ; + if (wait_pid(pid, &wstat) < 0) strerr_diefu1sys(111, "wait_pid") ; + cont = not == isbreak(breakcodes, nbc, wait_status(wstat)) ; + } + return WIFSIGNALED(wstat) ? WTERMSIG(wstat) : 0 ; +} diff --git a/src/execline/multidefine.c b/src/execline/multidefine.c new file mode 100644 index 0000000..9665418 --- /dev/null +++ b/src/execline/multidefine.c @@ -0,0 +1,12 @@ +/* ISC license. */ + +#include +#include "exlsn.h" + +#define USAGE "multidefine [ -0 ] [ -r ] [ -n ] [ -C | -c ] [ -d delim ] value { vars... } prog..." + +int main (int argc, char const **argv, char const *const *envp) +{ + PROG = "multidefine" ; + exlsn_main(argc, argv, envp, &exlsn_multidefine, USAGE) ; +} diff --git a/src/execline/multisubstitute.c b/src/execline/multisubstitute.c new file mode 100644 index 0000000..acee42f --- /dev/null +++ b/src/execline/multisubstitute.c @@ -0,0 +1,64 @@ +/* ISC license. */ + +#include +#include +#include +#include "exlsn.h" + +#define USAGE "see http://skarnet.org/software/execline/multisubstitute.html" + +static char const *const commands[8] = +{ + "define", + "importas", + "import", + "elglob", + "elgetpositionals", + "multidefine", + 0 +} ; + +static exlsnfunc_t *const functions[8] = +{ + &exlsn_define, + &exlsn_importas, + &exlsn_import, + &exlsn_elglob, + &exlsn_exlp, + &exlsn_multidefine, + 0 +} ; + +int main (int argc, char const **argv, char const *const *envp) +{ + exlsn_t info = EXLSN_ZERO ; + int argc1 ; + PROG = "multisubstitute" ; + if (!--argc) strerr_dieusage(100, USAGE) ; + + /* Read a block containing directives */ + argc1 = el_semicolon(++argv) ; + if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ; + if (argc1 + 1 == argc) strerr_dieusage(100, USAGE) ; + + /* Parse args and update the substitution info */ + while (argc1) + { + int n ; + unsigned int i = 0 ; + for (; commands[i] ; i++) if (!str_diff(*argv, commands[i])) break ; + if (!commands[i]) strerr_dief3x(100, "syntax error: unrecognized", " directive ", *argv) ; + n = (*(functions[i]))(argc1, argv, envp, &info) ; + if (n < 0) switch (n) + { + case -3 : strerr_dief3x(100, "syntax error at", " directive ", commands[i]) ; + case -2 : strerr_dief3x(100, "wrong key for", " directive ", commands[i]) ; + case -1 : strerr_diefu3sys(111, "run", " directive ", commands[i]) ; + default : strerr_dief3x(111, "unknown error with", " directive ", commands[i]) ; + } + argv += n ; argc1 -= n ; argc -= n ; + } + + /* Perform the substitution and exec */ + el_substandrun(argc-1, argv+1, envp, &info) ; +} diff --git a/src/execline/pipeline.c b/src/execline/pipeline.c new file mode 100644 index 0000000..42a230b --- /dev/null +++ b/src/execline/pipeline.c @@ -0,0 +1,85 @@ +/* ISC license. */ + +#include +#include +#ifdef EXECLINE_OLD_VARNAMES +#include +#endif +#include +#include +#include +#include +#include +#include + +#define USAGE "pipeline [ -d ] [ -r | -w ] { command... } command..." +#define dieusage() strerr_dieusage(100, USAGE) + +int main (int argc, char const **argv, char const *const *envp) +{ + int df = 0, w = 0 ; + PROG = "pipeline" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "drw", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'd' : df = 1 ; break ; + case 'r' : w = 0 ; break ; + case 'w' : w = 1 ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + { + pid_t pid ; + int fd ; + int argc1 = el_semicolon(argv) ; + if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ; + if (argc1 + 1 == argc) strerr_dief1x(100, "empty remainder") ; + argv[argc1] = 0 ; + if (df) + { + int p[2] ; + if (pipe(p) < 0) strerr_diefu1sys(111, "create pipe") ; + pid = doublefork() ; + switch (pid) + { + case -1: strerr_diefu1sys(111, "doublefork") ; + case 0: + PROG = "pipeline (grandchild)" ; + fd_close(p[w]) ; + if (fd_move(!w, p[!w]) < 0) strerr_diefu1sys(111, "fd_move") ; + pathexec0_run(argv, envp) ; + strerr_dieexec(127, argv[0]) ; + } + fd_close(p[!w]) ; + fd = p[w] ; + } + else + { + pid = el_spawn1(argv[0], argv, envp, &fd, !w) ; + if (!pid) strerr_diefu2sys(111, "spawn ", argv[0]) ; + } + if (fd_move(w, fd) < 0) strerr_diefu1sys(111, "fd_move") ; + { +#ifdef EXECLINE_OLD_VARNAMES + char fmt[UINT64_FMT * 2 + 10] = "!=" ; +#else + char fmt[UINT64_FMT + 2] = "!=" ; +#endif + register unsigned int i = 2 ; + i += uint64_fmt(fmt+i, (uint64)pid) ; fmt[i++] = 0 ; +#ifdef EXECLINE_OLD_VARNAMES + byte_copy(fmt+i, 8, "LASTPID=") ; i += 8 ; + i += uint64_fmt(fmt+i, (uint64)pid) ; fmt[i++] = 0 ; +#endif + pathexec_r(argv + argc1 + 1, envp, env_len(envp), fmt, i) ; + } + strerr_dieexec(111, argv[argc1 + 1]) ; + } +} diff --git a/src/execline/piperw.c b/src/execline/piperw.c new file mode 100644 index 0000000..9d24947 --- /dev/null +++ b/src/execline/piperw.c @@ -0,0 +1,29 @@ +/* ISC license. */ + +#include +#include +#include +#include + +#define USAGE "piperw fdr fdw prog..." + +int main (int argc, char const *const *argv, char const *const *envp) +{ + int fdr, fdw ; + int p[2] ; + PROG = "piperw" ; + if ((argc < 4) + || !uint0_scan(argv[1], (unsigned int *)&fdr) + || !uint0_scan(argv[2], (unsigned int *)&fdw) + || (fdr == fdw)) + strerr_dieusage(100, USAGE) ; + if (pipe(p) == -1) + strerr_diefu1sys(111, "create pipe") ; + if (p[1] == fdr) p[1] = dup(p[1]) ; + if ((p[1] == -1) + || (fd_move(fdr, p[0]) == -1) + || (fd_move(fdw, p[1]) == -1)) + strerr_diefu1sys(111, "move fds") ; + pathexec_run(argv[3], argv+3, envp) ; + strerr_dieexec(111, argv[3]) ; +} diff --git a/src/execline/redirfd.c b/src/execline/redirfd.c new file mode 100644 index 0000000..d9dca14 --- /dev/null +++ b/src/execline/redirfd.c @@ -0,0 +1,69 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include + +#define USAGE "redirfd -[ r | w | u | a | c | x ] [ -n ] [ -b ] fd file prog..." +#define dieusage() strerr_dieusage(100, USAGE) + +int main (int argc, char const *const *argv, char const *const *envp) +{ + int fd, fd2 ; + unsigned int flags = 0 ; + int what = -1 ; + int changemode = 0 ; + PROG = "redirfd" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "rwuacxnb", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'r' : what = O_RDONLY ; flags &= ~(O_APPEND|O_CREAT|O_TRUNC|O_EXCL) ; break ; + case 'w' : what = O_WRONLY ; flags |= O_CREAT|O_TRUNC ; flags &= ~(O_APPEND|O_EXCL) ; break ; + case 'u' : what = O_RDWR ; flags &= ~(O_APPEND|O_CREAT|O_TRUNC|O_EXCL) ; break ; + case 'a' : what = O_WRONLY ; flags |= O_CREAT|O_APPEND ; flags &= ~(O_TRUNC|O_EXCL) ; break ; + case 'c' : what = O_WRONLY ; flags |= O_APPEND ; flags &= ~(O_CREAT|O_TRUNC|O_EXCL) ; break ; + case 'x' : what = O_WRONLY ; flags |= O_CREAT|O_EXCL ; flags &= ~(O_APPEND|O_TRUNC) ; break ; + case 'n' : flags |= O_NONBLOCK ; break ; + case 'b' : changemode = 1 ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if ((argc < 3) || (what == -1)) dieusage() ; + if (!uint0_scan(argv[0], (unsigned int *)&fd)) dieusage() ; + flags |= what ; + fd2 = open3(argv[1], flags, 0666) ; + if ((fd2 == -1) && (what == O_WRONLY) && (errno == ENXIO)) + { + register int e ; + int fdr = open_read(argv[1]) ; + if (fdr == -1) strerr_diefu2sys(111, "open_read ", argv[1]) ; + fd2 = open3(argv[1], flags, 0666) ; + e = errno ; + fd_close(fdr) ; + errno = e ; + } + if (fd2 == -1) strerr_diefu2sys(111, "open ", argv[1]) ; + if (fd_move(fd, fd2) == -1) + { + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, fd2)] = 0 ; + strerr_diefu4sys(111, "move fd ", fmt, " to fd ", argv[0]) ; + } + if (changemode) + { + if (((flags & O_NONBLOCK) ? ndelay_off(fd) : ndelay_on(fd)) < 0) + strerr_diefu1sys(111, "change blocking mode") ; + } + pathexec_run(argv[2], argv+2, envp) ; + strerr_dieexec(111, argv[2]) ; +} diff --git a/src/execline/runblock.c b/src/execline/runblock.c new file mode 100644 index 0000000..a654023 --- /dev/null +++ b/src/execline/runblock.c @@ -0,0 +1,149 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "runblock [ -P ] [ -n argshift ] [ -r ] n" +#define dieusage() strerr_dieusage(100, USAGE) + +int main (int argc, char const *const *argv, char const *const *envp) +{ + genalloc v = GENALLOC_ZERO ; /* array of char const * */ + unsigned int n, sharp ; + unsigned int m = 0 ; + unsigned int strict = el_getstrict() ; + int flagnopop = 0, flagr = 0 ; + PROG = "runblock" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "Prn:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'P' : flagnopop = 1 ; break ; + case 'r' : flagr = 1 ; break ; + case 'n' : if (!uint0_scan(l.arg, &m)) dieusage() ; break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (argc != 1) dieusage() ; + if (!uint0_scan(argv[0], &n) || (!n && !flagr)) dieusage() ; + + { + char const *x = env_get2(envp, "#") ; + if (!x) strerr_dienotset(100, "#") ; + if (!uint0_scan(x, &sharp)) strerr_dieinvalid(100, "#") ; + } + + /* Skip n-1 blocks (n if flagr) */ + { + register unsigned int i = 1 ; + for (; i < n + flagr ; i++) + { + unsigned int base = m ; + for (;;) + { + char const *x ; + char fmt[UINT_FMT] ; + if (++m > sharp) strerr_dief1x(100, "too few arguments") ; + fmt[uint_fmt(fmt, m)] = 0 ; + x = env_get2(envp, fmt) ; + if (!x) strerr_dienotset(100, fmt) ; + if ((x[0] == EXECLINE_BLOCK_END_CHAR) && (!EXECLINE_BLOCK_END_CHAR || !x[1])) break ; + if ((x[0] != EXECLINE_BLOCK_QUOTE_CHAR) && strict) + { + char fmtb[UINT_FMT] ; + char fmti[UINT_FMT] ; + fmti[uint_fmt(fmti, i)] = 0 ; + fmtb[uint_fmt(fmtb, m - base)] = 0 ; + if (strict == 1) + strerr_warnw6x("unquoted positional ", x, " at block ", fmti, " position ", fmtb) ; + else + strerr_dief6x(100, "unquoted positional ", x, " at block ", fmti, " position ", fmtb) ; + } + } + } + } + + if (flagr) /* put remainder envvars into v */ + { + if (++m == sharp) return 0 ; + if (!genalloc_ready(char const *, &v, sharp - m + 2)) + strerr_diefu1sys(111, "genalloc_ready") ; + for (; m <= sharp ; m++) + { + char const *x ; + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, m)] = 0 ; + x = env_get2(envp, fmt) ; + if (!x) strerr_dienotset(100, fmt) ; + genalloc_append(char const *, &v, &x) ; + } + } + else /* put envvars from nth block into v */ + { + unsigned int base = m ; + for (;;) + { + char const *x ; + char fmt[UINT_FMT] ; + if (++m > sharp) strerr_dief1x(100, "too few arguments") ; + fmt[uint_fmt(fmt, m)] = 0 ; + x = env_get2(envp, fmt) ; + if (!x) strerr_dienotset(100, fmt) ; + if ((x[0] == EXECLINE_BLOCK_END_CHAR) && (!EXECLINE_BLOCK_END_CHAR || !x[1])) break ; + if (x[0] != EXECLINE_BLOCK_QUOTE_CHAR) + { + if (strict) + { + char fmtb[UINT_FMT] ; + char fmtn[UINT_FMT] ; + fmtn[uint_fmt(fmtn, n)] = 0 ; + fmtb[uint_fmt(fmtb, m - base)] = 0 ; + if (strict == 1) + strerr_warnw6x("unquoted positional ", x, " at block ", fmtn, " position ", fmtb) ; + else + strerr_dief6x(100, "unquoted positional ", x, " at block ", fmtn, " position ", fmtb) ; + } + } + else x++ ; + if (!genalloc_append(char const *, &v, &x)) strerr_diefu1sys(111, "envalloc_catb") ; + } + } + + { + char const *z = 0 ; + if (!genalloc_append(char const *, &v, &z)) + strerr_diefu1sys(111, "genalloc_append") ; + } + + if (flagnopop) /* exec now */ + pathexec_run(genalloc_s(char const *, &v)[0], genalloc_s(char const *, &v), envp) ; + else /* popenv, then exec */ + { + char const *list[11] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "#" } ; + unsigned int envlen = env_len(envp) ; + register int popped = el_popenv(&satmp, envp, envlen, list, 11) ; + if (popped < 0) strerr_diefu1sys(111, "pop environment") ; + else + { + char const *w[envlen - popped + 1] ; + if (!env_make(w, envlen - popped, satmp.s, satmp.len)) + strerr_diefu1sys(111, "env_make") ; + w[envlen - popped] = 0 ; + pathexec_run(genalloc_s(char const *, &v)[0], genalloc_s(char const *, &v), w) ; + stralloc_free(&satmp) ; + } + } + strerr_dieexec(111, genalloc_s(char const *, &v)[0]) ; +} diff --git a/src/execline/shift.c b/src/execline/shift.c new file mode 100644 index 0000000..b09ba38 --- /dev/null +++ b/src/execline/shift.c @@ -0,0 +1,121 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include + +#define USAGE "shift [ -n arg# ] [ -b block# ] prog..." +#define dieusage() strerr_dieusage(100, USAGE) + +int main (int argc, char const *const *argv, char const *const *envp) +{ + unsigned int strict = el_getstrict() ; + unsigned int b = 0 ; + unsigned int n = 0 ; + unsigned int sharp ; + unsigned int i = 0 ; + PROG = "shift" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "n:b:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'n' : + if (!uint0_scan(l.arg, &n)) dieusage() ; + i = 1 ; + break ; + case 'b' : + if (!uint0_scan(l.arg, &b)) dieusage() ; + i = 1 ; + break ; + default : dieusage() ; + } + } + argc -= l.ind ; argv += l.ind ; + } + if (!argc) dieusage() ; + if (i) i = 0 ; else n = 1 ; + { + char const *x = env_get2(envp, "#") ; + if (!x) strerr_dienotset(100, "#") ; + if (!uint0_scan(x, &sharp)) strerr_dieinvalid(100, "#") ; + } + + + /* Shift n args */ + + if (n > sharp) + { + if (strict) + { + char fmtn[UINT_FMT] ; + char fmtsharp[UINT_FMT] ; + fmtn[uint_fmt(fmtn, n)] = 0 ; + fmtsharp[uint_fmt(fmtsharp, sharp)] = 0 ; + if (strict == 1) + strerr_warnwu5x("shift", " ", fmtn, " arguments: got ", fmtsharp) ; + else + strerr_diefu5x(100, "shift", " ", fmtn, " arguments: got ", fmtsharp) ; + } + n = sharp ; + } + + + /* Shift b blocks */ + + for (; i < b ; i++) + { + for (;;) + { + char const *x ; + unsigned int base = n ; + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, ++n)] = 0 ; + if (n > sharp) + { + char fmti[UINT_FMT] ; + fmti[uint_fmt(fmt, i)] = 0 ; + strerr_diefu6x(100, "shift", " block ", fmti, ": too few arguments (", fmt, ")") ; + } + x = env_get2(envp, fmt) ; + if (!x) strerr_dienotset(100, fmt) ; + if ((x[0] == EXECLINE_BLOCK_END_CHAR) && (!EXECLINE_BLOCK_END_CHAR || !x[1])) break ; + if ((x[0] != EXECLINE_BLOCK_QUOTE_CHAR) && strict) + { + char fmti[UINT_FMT] ; + char fmtp[UINT_FMT] ; + fmti[uint_fmt(fmti, i)] = 0 ; + fmtp[uint_fmt(fmtp, n - base)] = 0 ; + if (strict == 1) + strerr_warnw6x("unquoted positional ", x, " at block ", fmti, " position ", fmtp) ; + else + strerr_dief6x(100, "unquoted positional ", x, " at block ", fmti, " position ", fmtp) ; + } + } + } + + + /* n = shift value; modify the env */ + + { + register unsigned int i = 1 ; + char fmt[UINT_FMT] ; + fmt[uint_fmt(fmt, sharp - n)] = 0 ; + if (!pathexec_env("#", fmt)) strerr_diefu1sys(111, "pathexec_env") ; + for (; i <= sharp ; i++) + { + char fmu[UINT_FMT] ; + fmt[uint_fmt(fmt, i)] = 0 ; + fmu[uint_fmt(fmu, i + n)] = 0 ; + if (!pathexec_env(fmt, i <= (sharp - n) ? env_get2(envp, fmu) : 0)) + strerr_diefu1sys(111, "pathexec_env") ; + } + } + pathexec(argv) ; + strerr_dieexec(111, argv[0]) ; +} diff --git a/src/execline/tryexec.c b/src/execline/tryexec.c new file mode 100644 index 0000000..46d3479 --- /dev/null +++ b/src/execline/tryexec.c @@ -0,0 +1,61 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include + +#define USAGE "tryexec [ -n ] [ -c ] [ -l ] [ -a argv0 ] { command... }" + +int main (int argc, char const **argv, char const *const *envp) +{ + char const *env_zero[1] = { 0 } ; + char const *executable = 0 ; + char const *argv0 = 0 ; + char const **dom = 0 ; + char const **sub = 0 ; + char const *const *dom_envp = envp ; + int not = 0, dash = 0 ; + PROG = "tryexec" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "ncla:", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'n' : not = 1 ; break ; + case 'c' : dom_envp = env_zero ; break ; + case 'l' : dash = 1 ; break ; + case 'a' : argv0 = l.arg ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + { + int argc1 = el_semicolon(argv) ; + if (!argc1) strerr_dief1x(100, "empty block") ; + if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ; + argv[argc1] = 0 ; + dom = argv + not * (argc1 + 1) ; + sub = argv + !not * (argc1 + 1) ; + } + executable = dom[0] ; + if (argv0) dom[0] = argv0 ; + if (dash) + { + register unsigned int n = str_len(dom[0]) ; + char dashed[n+2] ; + dashed[0] = '-' ; + byte_copy(dashed+1, n+1, dom[0]) ; + dom[0] = dashed ; + pathexec_run(executable, dom, dom_envp) ; + } + else pathexec_run(executable, dom, dom_envp) ; + + pathexec0_run(sub, envp) ; + strerr_dieexec(111, sub[0]) ; +} diff --git a/src/execline/umask.c b/src/execline/umask.c new file mode 100644 index 0000000..81217b6 --- /dev/null +++ b/src/execline/umask.c @@ -0,0 +1,20 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include + +#define USAGE "umask value prog..." + +int main (int argc, char const *const *argv, char const *const *envp) +{ + unsigned int m ; + PROG = "umask" ; + if (argc < 3) strerr_dieusage(100, USAGE) ; + if (!uint_oscan(argv[1], &m)) strerr_dieusage(100, USAGE) ; + umask(m) ; + pathexec_run(argv[2], argv+2, envp) ; + strerr_dieexec(111, argv[2]) ; +} diff --git a/src/execline/unexport.c b/src/execline/unexport.c new file mode 100644 index 0000000..189249c --- /dev/null +++ b/src/execline/unexport.c @@ -0,0 +1,20 @@ +/* ISC license. */ + +#include +#include +#include +#include + +#define USAGE "unexport variable prog..." + +int main (int argc, char const *const *argv, char const *const *envp) +{ + unsigned int len ; + PROG = "unexport" ; + if (argc < 3) strerr_dieusage(100, USAGE) ; + len = str_len(argv[1]) ; + if (byte_chr(argv[1], len, '=') < len) + strerr_dief2x(100, "invalid variable name: ", argv[1]) ; + pathexec_r(argv+2, envp, env_len(envp), argv[1], len+1) ; + strerr_dieexec(111, argv[2]) ; +} diff --git a/src/execline/wait.c b/src/execline/wait.c new file mode 100644 index 0000000..4d550c9 --- /dev/null +++ b/src/execline/wait.c @@ -0,0 +1,62 @@ +/* ISC license. */ + +#include +#include +#include +#include +#include +#include +#include + +#define USAGE "wait [ -r ] { pids... }" + +static unsigned int waitall (void) +{ + register unsigned int n = 0 ; + int wstat ; + while (wait(&wstat) > 0) n++ ; + return n ; +} + +int main (int argc, char const **argv, char const *const *envp) +{ + int argc1 ; + int flagreap = 0 ; + PROG = "wait" ; + { + subgetopt_t l = SUBGETOPT_ZERO ; + for (;;) + { + register int opt = subgetopt_r(argc, argv, "r", &l) ; + if (opt == -1) break ; + switch (opt) + { + case 'r' : flagreap = 1 ; break ; + default : strerr_dieusage(100, USAGE) ; + } + } + argc -= l.ind ; argv += l.ind ; + } + argc1 = el_semicolon(argv) ; + if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ; + if (!argc1) flagreap ? wait_reap() : waitall() ; + else + { + pid_t tab[argc1] ; + register unsigned int i = 0 ; + for (; i < (unsigned int)argc1 ; i++) + { + unsigned int pid ; + if (!uint0_scan(argv[i], &pid)) strerr_dieusage(100, USAGE) ; + tab[i] = pid ; + } + if (flagreap) + { + if (waitn_reap(tab, argc1) < 0) + strerr_diefu1sys(111, "waitn_reap") ; + } + else if (!waitn(tab, argc1)) strerr_diefu1sys(111, "waitn") ; + } + pathexec0_run(argv + argc1 + 1, envp) ; + strerr_dieexec(111, argv[argc1 + 1]) ; +} diff --git a/src/include-local/exlsn.h b/src/include-local/exlsn.h new file mode 100644 index 0000000..3665bca --- /dev/null +++ b/src/include-local/exlsn.h @@ -0,0 +1,36 @@ +/* ISC license. */ + +#ifndef EXLSN_H +#define EXLSN_H + +#include +#include + +typedef struct exlsn_s exlsn_t, *exlsn_t_ref ; +struct exlsn_s +{ + stralloc vars ; + stralloc values ; + stralloc data ; /* array of elsubst */ +} ; + +#define EXLSN_ZERO { .vars = STRALLOC_ZERO, .values = STRALLOC_ZERO, .data = STRALLOC_ZERO } + +extern void exlsn_free (exlsn_t *) ; + +typedef int exlsnfunc_t (int, char const **, char const *const *, exlsn_t *) ; +typedef exlsnfunc_t *exlsnfunc_t_ref ; + +extern exlsnfunc_t exlsn_define ; +extern exlsnfunc_t exlsn_importas ; +extern exlsnfunc_t exlsn_import ; +extern exlsnfunc_t exlsn_elglob ; +extern exlsnfunc_t exlsn_exlp ; +extern exlsnfunc_t exlsn_multidefine ; + +extern int exlp (unsigned int, char const *const *, exlsn_t *) ; +extern void el_substandrun (int, char const *const *, char const *const *, exlsn_t *) gccattr_noreturn ; +extern void el_substandrun_str (stralloc *, unsigned int, char const *const *, exlsn_t *) gccattr_noreturn ; +extern void exlsn_main (int, char const **, char const *const *, exlsnfunc_t *, char const *) gccattr_noreturn ; + +#endif diff --git a/src/include/execline/execline.h b/src/include/execline/execline.h new file mode 100644 index 0000000..a5c16ad --- /dev/null +++ b/src/include/execline/execline.h @@ -0,0 +1,67 @@ +/* ISC license. */ + +#ifndef EXECLINE_H +#define EXECLINE_H + +#include +#include +#include + +#define EXECLINE_BLOCK_QUOTE_CHAR ' ' +#define EXECLINE_BLOCK_END_CHAR '\0' + + +/* Basics */ + +extern int el_vardupl (char const *, char const *, unsigned int) gccattr_pure ; +extern unsigned int el_getstrict (void) gccattr_const ; +extern void el_obsolescent (void) ; + + +/* Environment shifting */ + +extern int el_pushenv (stralloc *, char const *const *, unsigned int, char const *const *, unsigned int) ; +extern int el_popenv (stralloc *, char const *const *, unsigned int, char const *const *, unsigned int) ; + + +/* Sequence */ + +extern pid_t el_spawn0 (char const *, char const *const *, char const *const *) ; +extern pid_t el_spawn1 (char const *, char const *const *, char const *const *, int *, int) ; +extern void el_execsequence (char const *const *, char const *const *, char const *const *) gccattr_noreturn ; + + +/* Block unquoting */ + +extern int el_semicolon (char const **) ; + + +/* Value transformation */ + +typedef struct eltransforminfo_s eltransforminfo_t, *eltransforminfo_t_ref ; +struct eltransforminfo_s +{ + char const *delim ; + unsigned int crunch : 1 ; + unsigned int chomp : 1 ; + unsigned int split : 1 ; +} ; + +#define ELTRANSFORMINFO_ZERO { .delim = " \n\r\t", .crunch = 0, .chomp = 0, .split = 0 } + +extern int el_transform (stralloc *, unsigned int, eltransforminfo_t const *) ; + + +/* Substitution */ + +typedef struct elsubst_s elsubst_t, *elsubst_t_ref ; +struct elsubst_s +{ + unsigned int var ; + unsigned int value ; + unsigned int n ; +} ; + +extern int el_substitute (stralloc *, char const *, unsigned int, char const *, char const *, elsubst_t const *, unsigned int) ; + +#endif 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 +#ifdef EXECLINE_OLD_VARNAMES +#include +#endif +#include +#include +#include +#include +#include + +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 +#include +#include + +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 +#include + +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 +#include +#include +#include +#include + +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 +#include +#include +#include +#include + +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 +#include +#include +#include + +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 +#include +#include + +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 +#include +#include + +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 +#include +#include +#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 +#include +#include +#include +#include +#include +#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 +#include +#include +#include + +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 +#include +#include +#include +#include + +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 +#include + +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 +#include +#include +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#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 +#include +#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 +#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 +#include +#include +#include +#include +#include +#include +#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 +#include +#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 +#include +#include +#include +#include +#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 ; +} diff --git a/tools/gen-deps.sh b/tools/gen-deps.sh new file mode 100755 index 0000000..af31259 --- /dev/null +++ b/tools/gen-deps.sh @@ -0,0 +1,79 @@ +#!/bin/sh -e + +. package/info + +echo '#' +echo '# This file has been generated by tools/gen-deps.sh' +echo '#' +echo + +for dir in src/include/${package} src/* ; do + for file in $(ls -1 $dir | grep -- \\.h$) ; do + { + grep -F -- "#include <${package}/" < ${dir}/$file | cut -d'<' -f2 | cut -d'>' -f1 ; + grep -- '#include ".*\.h"' < ${dir}/$file | cut -d'"' -f2 + } | sort -u | { + deps= + while read dep ; do + if echo $dep | grep -q "^${package}/" ; then + deps="$deps src/include/$dep" + elif test -f "${dir}/$dep" ; then + deps="$deps ${dir}/$dep" + else + deps="$deps src/include-local/$dep" + fi + done + if test -n "$deps" ; then + echo "${dir}/${file}:${deps}" + fi + } + done +done + +for dir in src/* ; do + for file in $(ls -1 $dir | grep -- \\.c$) ; do + { + grep -F -- "#include <${package}/" < ${dir}/$file | cut -d'<' -f2 | cut -d'>' -f1 ; + grep -- '#include ".*\.h"' < ${dir}/$file | cut -d'"' -f2 + } | sort -u | { + deps=" ${dir}/$file" + while read dep ; do + if echo $dep | grep -q "^${package}/" ; then + deps="$deps src/include/$dep" + elif test -f "${dir}/$dep" ; then + deps="$deps ${dir}/$dep" + else + deps="$deps src/include-local/$dep" + fi + done + o=$(echo $file | sed s/\\.c$/.o/) + lo=$(echo $file | sed s/\\.c$/.lo/) + echo "${dir}/${o} ${dir}/${lo}:${deps}" + } + done +done +echo + +for dir in $(ls -1 src | grep -v ^include) ; do + for file in $(ls -1 src/$dir/deps-lib) ; do + deps= + while read dep ; do + deps="$deps src/$dir/$dep" + done < src/$dir/deps-lib/$file + echo "lib$file.a: $deps" + if test -x "src/$dir/deps-lib/$file" ; then + echo "lib${file}.so: $(echo "$deps" | sed 's/\.o/.lo/g')" + fi + done + + for file in $(ls -1 src/$dir/deps-exe) ; do + deps= + while read dep ; do + if echo $dep | grep -q -- \\.o$ ; then + dep="src/$dir/$dep" + fi + deps="$deps $dep" + done < src/$dir/deps-exe/$file + echo "$file: src/$dir/$file.o$deps" + done +done diff --git a/tools/install.sh b/tools/install.sh new file mode 100755 index 0000000..89f9428 --- /dev/null +++ b/tools/install.sh @@ -0,0 +1,64 @@ +#!/bin/sh + +usage() { + echo "usage: $0 [-D] [-l] [-m mode] src dst" 1>&2 + exit 1 +} + +mkdirp=false +symlink=false +mode=0755 + +while getopts Dlm: name ; do + case "$name" in + D) mkdirp=true ;; + l) symlink=true ;; + m) mode=$OPTARG ;; + ?) usage ;; + esac +done +shift $(($OPTIND - 1)) + +test "$#" -eq 2 || usage +src=$1 +dst=$2 +tmp="$dst.tmp.$$" + +case "$dst" in + */) echo "$0: $dst ends in /" 1>&2 ; exit 1 ;; +esac + +set -C +set -e + +if $mkdirp ; then + umask 022 + case "$2" in + */*) mkdir -p "${dst%/*}" ;; + esac +fi + +trap 'rm -f "$tmp"' EXIT INT QUIT TERM HUP + +umask 077 + +if $symlink ; then + ln -s "$src" "$tmp" +else + cat < "$1" > "$tmp" + chmod "$mode" "$tmp" +fi + +mv -f "$tmp" "$dst" +if test -d "$dst" ; then + rm -f "$dst/$(basename $tmp)" + if $symlink ; then + mkdir "$tmp" + ln -s "$src" "$tmp/$(basename $dst)" + mv -f "$tmp/$(basename $dst)" "${dst%/*}" + rmdir "$tmp" + else + echo "$0: $dst is a directory" 1>&2 + exit 1 + fi +fi -- cgit v1.2.3