summary refs log tree commit diff
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2019-12-24 14:27:51 +0000
committerLaurent Bercot <ska-skaware@skarnet.org>2019-12-24 14:27:51 +0000
commitd9b6a5820f195ef681d7cd15d70a184265b37a94 (patch)
tree49f952f89c5e6aa0992a0e45fdba636ab0d2bbca
parent2a96bc93c24f34a972740da3e7df031a2d36e7cb (diff)
downloadexecline-d9b6a5820f195ef681d7cd15d70a184265b37a94.tar.gz
execline-d9b6a5820f195ef681d7cd15d70a184265b37a94.tar.xz
execline-d9b6a5820f195ef681d7cd15d70a184265b37a94.zip
Add posix-umask; prepare for 2.6.0.0
-rw-r--r--NEWS10
-rw-r--r--doc/index.html3
-rw-r--r--doc/posix-cd.html2
-rw-r--r--doc/posix-umask.html69
-rw-r--r--doc/umask.html11
-rw-r--r--doc/upgrade.html8
-rw-r--r--package/deps.mak3
-rw-r--r--package/info2
-rw-r--r--package/modes1
-rw-r--r--package/targets.mak5
-rw-r--r--src/posix/deps-exe/posix-umask1
-rw-r--r--src/posix/posix-umask.c174
-rw-r--r--src/posix/posix-umask.txt37
13 files changed, 318 insertions, 8 deletions
diff --git a/NEWS b/NEWS
index c614ec0..7ae513e 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,15 @@
 Changelog for execline.
 
+In 2.6.0.0
+----------
+
+ - The dollarat program now has its conflicting -0 and -d options
+handled in the conventional way, with rightmost priority.
+ - New binary: posix-umask. With --enable-pedantic-posix,
+umask is a symbolic link to posix-umask. And, hopefully, this
+completes the "make execline POSIX-compliant" chapter.
+
+
 In 2.5.3.0
 ----------
 
diff --git a/doc/index.html b/doc/index.html
index 0a3643c..d93f704 100644
--- a/doc/index.html
+++ b/doc/index.html
@@ -66,7 +66,7 @@ library. </li>
 <h3> Download </h3>
 
 <ul>
- <li> The current released version of execline is <a href="execline-2.5.3.0.tar.gz">2.5.3.0</a>. </li>
+ <li> The current released version of execline is <a href="execline-2.6.0.0.tar.gz">2.6.0.0</a>. </li>
  <li> Alternatively, you can checkout a copy of the
 <a href="//git.skarnet.org/cgi-bin/cgit.cgi/execline/">execline
 git repository</a>:
@@ -128,6 +128,7 @@ to your installation: the shebang lines for your system might be something like
 <li><a href="cd.html">The <tt>cd</tt> program</a></li>
 <li><a href="posix-cd.html">The <tt>posix-cd</tt> program</a></li>
 <li><a href="umask.html">The <tt>umask</tt> program</a></li>
+<li><a href="posix-umask.html">The <tt>posix-umask</tt> program</a></li>
 <li><a href="emptyenv.html">The <tt>emptyenv</tt> program</a></li>
 <li><a href="envfile.html">The <tt>envfile</tt> program</a></li>
 <li><a href="export.html">The <tt>export</tt> program</a></li>
diff --git a/doc/posix-cd.html b/doc/posix-cd.html
index 3a24602..5074658 100644
--- a/doc/posix-cd.html
+++ b/doc/posix-cd.html
@@ -26,7 +26,7 @@ given directory, then executes a program.
 <h2> Interface </h2>
 
 <pre>
-     posix-cd <em>dir</em> <em>prog...</em>
+     posix-cd [ -L | -P ] <em>dir</em> <em>prog...</em>
 </pre>
 
 <p>
diff --git a/doc/posix-umask.html b/doc/posix-umask.html
new file mode 100644
index 0000000..4ee310d
--- /dev/null
+++ b/doc/posix-umask.html
@@ -0,0 +1,69 @@
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+ <meta http-equiv="Content-Language" content="en" />
+ <title>execline: the posix-umask command</title>
+ <meta name="Description" content="execline: the posix-umask command" />
+ <meta name="Keywords" content="execline command umask mask posix posix-umask" />
+ <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
+</head>
+<body>
+
+<p>
+<a href="index.html">execline</a><br />
+<a href="//skarnet.org/software/">Software</a><br />
+<a href="//skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>posix-umask</tt> program </h1>
+
+<p>
+<tt>posix-umask</tt> changes its file mode creation mask, then executes a program.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+     posix-umask [ -S ] [ <em>mask</em> ] [ <em>prog...</em> ]
+</pre>
+
+<p>
+When called with no argument, <tt>posix-umask</tt> prints the value of the
+file mode creation mask of the invoking process, then exits 0.
+</p>
+
+<p>
+ When called with a <em>mask</em> argument, <tt>posix-umask</tt> changes
+its file mode creation mask; then, if <em>prog...</em> is not empty, it execs
+into it.
+</p>
+
+<p>
+ <tt>posix-umask</tt> interprets <em>mask</em> as specified by the
+<a href="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/umask.html">POSIX
+specification for a <tt>umask</tt> external utility</a>.
+</p>
+
+<h2> Notes </h2>
+
+<ul>
+ <li> <tt>posix-umask</tt> is only available when execline has been configured
+with the <tt>--enable-pedantic-posix</tt> option, and in this case, the
+<a href="umask.html">cd</a> binary is a symbolic link to it. </li>
+ <li> <tt>posix-umask</tt> fully conforms to the
+<a href="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/umask.html">POSIX
+specification</a>. When <em>prog...</em> is not empty, the behaviour of a
+<tt>umask</tt> utility is not specified by POSIX, so <tt>posix-umask</tt> extends
+the spec to be actually useful and usable in an execline program with the same
+interface as the regular execline <a href="umask.html">umask</a> command. </li>
+ <li> Nobody ever executes or needs the external version (i.e. not a shell
+builtin) of the POSIX <tt>umask</tt> command. Compared to the regular execline
+<a href="umask.html">umask</a>, <tt>posix-umask</tt> is uselessly bloated and slow.
+The only reason it exists is that some distributions refuse to package
+execline correctly unless it is strictly POSIX-compliant; the
+<tt>--enable-pedantic-posix</tt> configure option is there to appease them. </li> 
+</ul>
+
+</body>
+</html>
diff --git a/doc/umask.html b/doc/umask.html
index 1acd04b..22ae137 100644
--- a/doc/umask.html
+++ b/doc/umask.html
@@ -36,10 +36,13 @@ then execs into <em>prog...</em>.
 
 <h2> Notes </h2>
 
-<p>
-<tt>umask</tt> is a standard shell builtin. Be careful if you want to
-use the <tt>umask</tt> command outside of an <tt>execline</tt> script.
-</p>
+<ul>
+<li> <tt>umask</tt> is a standard shell builtin. Be careful if you want to
+use the <tt>umask</tt> command outside of an <tt>execline</tt> script. </li>
+ <li> When execline has been configured with the <tt>--enable-pedantic-posix</tt>
+option, the <tt>umask</tt> binary is actually a symbolic link to the
+<a href="posix-umask.html">posix-umask</a> binary. </li>
+</ul>
 
 </body>
 </html>
diff --git a/doc/upgrade.html b/doc/upgrade.html
index 045803e..bbb10aa 100644
--- a/doc/upgrade.html
+++ b/doc/upgrade.html
@@ -18,6 +18,14 @@
 
 <h1> What has changed in execline </h1>
 
+<h2> in 2.6.0.0 </h2>
+
+<ul>
+ <li> <a href="dollarat.html">dollarat</a> now has its <tt>-0</tt> and <tt>-d</tt>
+priority unified. (Rightmost priority.) </li>
+ <li> New binary: <a href="posix-umask.html">posix-umask</a>. </li>
+</ul>
+
 <h2> in 2.5.3.0 </h2>
 
 <ul>
diff --git a/package/deps.mak b/package/deps.mak
index 6140333..f944bc7 100644
--- a/package/deps.mak
+++ b/package/deps.mak
@@ -72,6 +72,7 @@ src/libexecline/exlsn_importas.o src/libexecline/exlsn_importas.lo: src/libexecl
 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
 src/posix/posix-cd.o src/posix/posix-cd.lo: src/posix/posix-cd.c
+src/posix/posix-umask.o src/posix/posix-umask.lo: src/posix/posix-umask.c
 
 background: EXTRA_LIBS := -lskarnet ${SPAWN_LIB}
 background: src/execline/background.o ${LIBEXECLINE}
@@ -174,3 +175,5 @@ libexecline.so.xyzzy: EXTRA_LIBS := -lskarnet
 libexecline.so.xyzzy: src/libexecline/el_execsequence.lo src/libexecline/el_getstrict.lo src/libexecline/el_parse.lo src/libexecline/el_parse_from_buffer.lo src/libexecline/el_parse_from_string.lo src/libexecline/el_popenv.lo src/libexecline/el_pushenv.lo src/libexecline/el_semicolon.lo src/libexecline/el_spawn0.lo src/libexecline/el_spawn1.lo src/libexecline/el_substandrun.lo src/libexecline/el_substandrun_str.lo src/libexecline/el_substitute.lo src/libexecline/el_transform.lo src/libexecline/el_vardupl.lo src/libexecline/exlsn_define.lo src/libexecline/exlsn_elglob.lo src/libexecline/exlsn_importas.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
 posix-cd: EXTRA_LIBS := -lskarnet
 posix-cd: src/posix/posix-cd.o
+posix-umask: EXTRA_LIBS := -lskarnet
+posix-umask: src/posix/posix-umask.o
diff --git a/package/info b/package/info
index 4a5dd27..3361ae9 100644
--- a/package/info
+++ b/package/info
@@ -1,4 +1,4 @@
 package=execline
-version=2.5.3.0
+version=2.6.0.0
 category=admin
 package_macro_name=EXECLINE
diff --git a/package/modes b/package/modes
index dbe4972..204b67c 100644
--- a/package/modes
+++ b/package/modes
@@ -45,3 +45,4 @@ unexport		0755
 wait			0755
 withstdinas		0755
 posix-cd		0755
+posix-umask		0755
diff --git a/package/targets.mak b/package/targets.mak
index 824fc26..b502988 100644
--- a/package/targets.mak
+++ b/package/targets.mak
@@ -52,9 +52,12 @@ LIB_DEFS := EXECLINE=execline
 
 ifeq ($(PEDANTIC_POSIX),1)
 
-BIN_TARGETS += posix-cd
+BIN_TARGETS += posix-cd posix-umask
 
 $(DESTDIR)$(bindir)/cd: $(DESTDIR)$(bindir)/posix-cd
 	exec ./tools/install.sh -l posix-cd $(DESTDIR)$(bindir)/cd
 
+$(DESTDIR)$(bindir)/umask: $(DESTDIR)$(bindir)/posix-umask
+	exec ./tools/install.sh -l posix-umask $(DESTDIR)$(bindir)/umask
+
 endif
diff --git a/src/posix/deps-exe/posix-umask b/src/posix/deps-exe/posix-umask
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/posix/deps-exe/posix-umask
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/posix/posix-umask.c b/src/posix/posix-umask.c
new file mode 100644
index 0000000..b4b9b4b
--- /dev/null
+++ b/src/posix/posix-umask.c
@@ -0,0 +1,174 @@
+/* ISC license. */
+
+#include <stdint.h>
+#include <sys/stat.h>
+#include <locale.h>
+
+#include <skalibs/gccattributes.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/types.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/buffer.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/djbunix.h>
+
+#define USAGE "posix-umask [ -S ] [ mask ] [ prog... ]"
+#define dieusage() strerr_dieusage(100, USAGE)
+#define dieout() strerr_diefu1sys(111, "write to stdout")
+
+
+ /* well, unlike posix-cd, at least this one was fun to write */
+
+static inline int output (int sym)
+{
+  mode_t mode = umask(0) ;
+  size_t m = 0 ;
+  char fmt[18] ;
+  if (sym)
+  {
+    unsigned int i = 3 ;
+    while (i--)
+    {
+      unsigned int mask = ~(mode >> (3*i)) ;
+      fmt[m++] = "ogu"[i] ; fmt[m++] = '=' ;
+      if (mask & 4) fmt[m++] = 'r' ;
+      if (mask & 2) fmt[m++] = 'w' ;
+      if (mask & 1) fmt[m++] = 'x' ;
+      if (i) fmt[m++] = ',' ;
+    }
+  }
+  else m += uint0_ofmt(fmt, mode, 4) ;
+  fmt[m++] = '\n' ;
+  if (buffer_putflush(buffer_1, fmt, m) < 0) dieout() ;
+  return 0 ;
+}
+
+static void diesyntax (char const *) gccattr_noreturn ;
+static void diesyntax (char const *s)
+{
+  strerr_dief3x(101, "internal parsing error: bad ", s, ". Please submit a bug-report.") ;
+}
+
+static inline uint8_t cclass (char c)
+{
+ /* char tables may be more efficient, but this is way more readable */
+  switch (c)
+  {
+    case 0 : return 0 ;
+    case ',' : return 1 ;
+    case '+' :
+    case '-' :
+    case '=' : return 2 ;
+    case 'u' :
+    case 'g' :
+    case 'o' : return 3 ;
+    case 'a' : return 4 ;
+    case 'r' :
+    case 'w' :
+    case 'x' :
+    case 'X' :
+    case 's' :
+    case 't' : return 5 ;
+    default : return 6 ;
+  }
+}
+
+static inline uint8_t who_value (char c)
+{
+  switch (c)
+  {
+    case 'u' : return 4 ;
+    case 'g' : return 2 ;
+    case 'o' : return 1 ;
+    case 'a' :
+    case '+' : /* shortcut for when who is empty */
+    case '-' :
+    case '=' : return 7 ;
+    default : diesyntax("who") ;
+  }
+}
+
+static inline uint8_t perm_value (char c)
+{
+  switch (c)
+  {
+    case 'r' : return 4 ;
+    case 'w' : return 2 ;
+    case 'x' :
+    case 'X' : return 1 ;
+    case 's' :
+    case 't' : return 0 ;
+    default : diesyntax("perm") ;
+  }
+}
+
+static inline unsigned int parsemode (char const *s)
+{
+  static uint16_t const table[5][7] =
+  {
+    { 0x005, 0x000, 0x064, 0x021, 0x021, 0x006, 0x006 },
+    { 0x005, 0x006, 0x042, 0x021, 0x021, 0x006, 0x006 },
+    { 0x005, 0x200, 0x042, 0x083, 0x006, 0x104, 0x006 },
+    { 0x805, 0xe00, 0xc42, 0x006, 0x006, 0x006, 0x006 },
+    { 0x805, 0xe00, 0xc42, 0x006, 0x006, 0x104, 0x006 }
+  } ;
+  unsigned int oldmode = ~umask(0) ;
+  uint8_t modes[3] = { oldmode & 7, (oldmode >> 3) & 7, (oldmode >> 6) & 7 } ;
+  uint8_t who = 0 ;
+  uint8_t perm = 0 ;
+  uint8_t state = 0 ;
+  char op = 0 ;
+  while (state < 5)
+  {
+    char c = *s++ ;
+    uint16_t what = table[state][cclass(c)] ;
+    state = what & 7 ;
+    if (what & 0x020) who |= who_value(c) ;
+    if (what & 0x080) perm = modes[byte_chr("ogu", 3, c)] ;
+    if (what & 0x100) perm |= perm_value(c) ;
+    if (what & 0x800)
+    {
+      unsigned int i = 3 ;
+      while (i--) if (who & (1 << i))
+        switch (op)
+        {
+          case '-' : modes[i] &= ~perm ; break ;
+          case '+' : modes[i] |= perm ; break ;
+          case '=' : modes[i] = perm ; break ;
+          default : diesyntax("op") ;
+        }
+    }
+    if (what & 0x040) op = c ;
+    if (what & 0x200) who = 0 ;
+    if (what & 0x400) perm = 0 ;
+  }
+  if (state > 5) strerr_dief1x(1, "invalid mode string") ;
+  return ((unsigned int)modes[2] << 6) | ((unsigned int)modes[1] << 3) | modes[0] ;
+}
+
+int main (int argc, char const **argv, char const *const *envp)
+{
+  int sym = 0 ;
+  unsigned int mode ;
+  PROG = "posix-umask" ;
+  setlocale(LC_ALL, "") ;  /* totally supported, I swear */
+
+  {
+    subgetopt_t l = SUBGETOPT_ZERO ;
+    for (;;)
+    {
+      int opt = subgetopt_r(argc, argv, "S", &l) ;
+      if (opt == -1) break ;
+      switch (opt)
+      {
+        case 'S' : sym = 1 ; break ;
+        default : dieusage() ;
+      }
+    }
+    argc -= l.ind ; argv += l.ind ;
+  }
+  if (!argc) return output(sym) ;
+  if (!uint0_oscan(argv[0], &mode)) mode = ~parsemode(argv[0]) ;
+  umask(mode & 00777) ;
+  xpathexec0_run(argv+1, envp) ;
+}
diff --git a/src/posix/posix-umask.txt b/src/posix/posix-umask.txt
new file mode 100644
index 0000000..5bf49a2
--- /dev/null
+++ b/src/posix/posix-umask.txt
@@ -0,0 +1,37 @@
+
+ parsemode() function for posix-umask
+
+ goal: parse the "u+r,g-wx,o=u" symbolic mode string and convert it
+to a numeric value suitable for umask().
+ In the purest skarnet.org tradition, we implement the parser via a DFA.
+
+class	|	0	1	2	3	4	5	6
+st\ev	|	\0	,	+-=	ugo	a	rwxXst	other
+------------------------------------------------------------------------
+START	|			wo	w	w
+0	|	END	START	OP	WHO	WHO	X	X
+
+WHO	|			o	w	w
+1	|	END	X	OP	WHO	WHO	X	X
+
+OP	|		r	o	c		p
+2	|	END	START	OP	PERMCPY	X	PERM	X
+
+PERMCPY	|	!	!rR	!Ro	
+3	|	END	START	OP	X	X	X	X
+
+PERM	|	!	!rR	!Ro			p
+4	|	END	START	OP	X	X	PERM	X
+------------------------------------------------------------------------
+
+END=5, X=6. -> states: 3 bits
+7 actions -> 10 bits total, need uint16_t
+
+ w: 0x020: who |= c
+ o: 0x040: store op
+ c: 0x080: copy perm from c
+ p: 0x100: perm |= c
+ r: 0x200: reset who
+ R: 0x400: reset perm
+ !: 0x800: apply (who, op, perm) change
+