Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package jo for openSUSE:Factory checked in 
at 2022-01-11 21:20:21
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/jo (Old)
 and      /work/SRC/openSUSE:Factory/.jo.new.1892 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "jo"

Tue Jan 11 21:20:21 2022 rev:6 rq:945611 version:1.6

Changes:
--------
--- /work/SRC/openSUSE:Factory/jo/jo.changes    2021-05-18 18:27:51.894592503 
+0200
+++ /work/SRC/openSUSE:Factory/.jo.new.1892/jo.changes  2022-01-11 
21:24:33.565182069 +0100
@@ -1,0 +2,20 @@
+Wed Jan  5 16:14:18 UTC 2022 - Martin Hauke <mar...@gmx.de>
+
+- Update to versino 1.6
+  * FIX: repair tests broken by AUTHORS change
+  * FIX: repair make distcheck by removing copied _jo zsh functions
+
+-------------------------------------------------------------------
+Tue Jan  4 11:59:53 UTC 2022 - Martin Hauke <mar...@gmx.de>
+
+- Udpate to version 1.5
+  * NEW: replace asserts with human errors
+  * NEW: zsh completion
+  * FIX: several cleanups
+  * NEW: Meson build
+  * NEW: option to deduplicate keys
+  * NEW: Filter functionality
+  * FIX: file embedding
+  * FIX: add missing tests to Makefile.am
+
+-------------------------------------------------------------------

Old:
----
  jo-1.4.tar.gz

New:
----
  jo-1.6.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ jo.spec ++++++
--- /var/tmp/diff_new_pack.v4v0H9/_old  2022-01-11 21:24:33.957182345 +0100
+++ /var/tmp/diff_new_pack.v4v0H9/_new  2022-01-11 21:24:33.961182347 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package jo
 #
-# Copyright (c) 2021 SUSE LLC
+# Copyright (c) 2022 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -17,7 +17,7 @@
 
 
 Name:           jo
-Version:        1.4
+Version:        1.6
 Release:        0
 Summary:        JSON output from a shell
 License:        GPL-2.0-or-later AND MIT
@@ -37,12 +37,21 @@
 Group:          Productivity/Text/Utilities
 Requires:       %{name} = %{version}
 Requires:       bash-completion
-Supplements:    (jo and bash-completion)
+Supplements:    (%{name} and bash-completion)
 BuildArch:      noarch
 
 %description bash-completion
 Bash completion script for %{name}.
 
+%package zsh-completion
+Summary:        ZSH completion for %{name}
+Group:          Productivity/Text/Utilities
+Supplements:    (%{name} and zsh)
+BuildArch:      noarch
+
+%description zsh-completion
+zsh shell completions for %{name}.
+
 %prep
 %setup -q
 
@@ -54,6 +63,8 @@
 
 %install
 %make_install
+install -d %{buildroot}/%{_sysconfdir}/zsh_completion.d
+mv %{buildroot}%{_datadir}/zsh/site-functions/_jo 
%{buildroot}%{_sysconfdir}/zsh_completion.d/%{name}
 
 %check
 %make_build check
@@ -67,4 +78,8 @@
 %files bash-completion
 %{_datadir}/bash-completion/completions/%{name}.bash
 
+%files zsh-completion
+%dir %{_sysconfdir}/zsh_completion.d
+%config %{_sysconfdir}/zsh_completion.d/%{name}
+
 %changelog

++++++ jo-1.4.tar.gz -> jo-1.6.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/.travis.yml new/jo-1.6/.travis.yml
--- old/jo-1.4/.travis.yml      2020-07-18 18:20:38.000000000 +0200
+++ new/jo-1.6/.travis.yml      2022-01-05 16:59:37.000000000 +0100
@@ -1,6 +1,10 @@
 os:
  - linux
  - osx
+
+arch:
+  - amd64
+  - ppc64le
 sudo: false
 language: c
 compiler:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/AUTHORS new/jo-1.6/AUTHORS
--- old/jo-1.4/AUTHORS  2020-07-18 18:20:38.000000000 +0200
+++ new/jo-1.6/AUTHORS  2022-01-05 16:59:37.000000000 +0100
@@ -1 +1,2 @@
 Jan-Piet Mens <jpm...@gmail.com>
+Adrian Ho <the.grom...@gmail.com>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/ChangeLog new/jo-1.6/ChangeLog
--- old/jo-1.4/ChangeLog        2020-07-18 18:20:38.000000000 +0200
+++ new/jo-1.6/ChangeLog        2022-01-05 16:59:37.000000000 +0100
@@ -1,3 +1,22 @@
+
+2022-01-05 1.6
+
+- FIX: repair tests broken by AUTHORS change (#164)
+- FIX: repair make distcheck by removing copied _jo zsh functions
+
+2022-01-04 1.5
+
+- NEW: replace asserts with human errors (#162)
+- NEW: zsh completion (#158)
+- FIX: stdin filter on Windows (#
+- FIX: several cleanups
+- NEW: Meson build
+- UPD: snap to newer base (#149)
+- NEW: option to deduplicate keys (#143, #145)
+- NEW: Filter functionality (#141)
+- FIX: file embedding
+- FIX: add missing tests to Makefile.am
+
 2020-07-18 1.4
 
 - FIX: Coercion flag logic now permits getopt(3) double-dash
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/Makefile.am new/jo-1.6/Makefile.am
--- old/jo-1.4/Makefile.am      2020-07-18 18:20:38.000000000 +0200
+++ new/jo-1.6/Makefile.am      2022-01-05 16:59:37.000000000 +0100
@@ -10,17 +10,25 @@
 bashcompdir = @bashcompdir@
 dist_bashcomp_DATA = jo.bash
 
+zshcompdir = $(datadir)/zsh/site-functions
+dist_zshcomp_DATA = jo.zsh
+install-data-hook:
+       mv -f $(DESTDIR)$(zshcompdir)/jo.zsh $(DESTDIR)$(zshcompdir)/_jo
+
+uninstall-local:
+       rm -f $(DESTDIR)$(zshcompdir)/_jo
+
 if USE_PANDOC
 # Add targets to rebuild pages
 jo.1: jo.pandoc
        @test -n "$(PANDOC)" || \
          { echo 'pandoc' not found during configure.; exit 1; }
-       $(PANDOC) -s -w man+simple_tables -o $@ $<
+       $(PANDOC) -s -w man -f markdown -o $@ $<
 
 jo.md: jo.pandoc
        @test -n "$(PANDOC)" || \
          { echo 'pandoc' not found during configure.; exit 1; }
-       $(PANDOC) -s -w markdown+simple_tables -o $@ $<
+       $(PANDOC) -s -w markdown+simple_tables -f markdown -o $@ $<
 
 endif
 
@@ -53,4 +61,17 @@
                  tests/jo.11.sh tests/jo.11.exp \
                  tests/jo.12.sh tests/jo.12.exp \
                  tests/jo.13.sh tests/jo.13.exp tests/jo-logo.png \
-                 tests/jo.14.sh tests/jo.14.exp 
+                 tests/jo.14.sh tests/jo.14.exp \
+                 tests/jo.15.sh tests/jo.15.exp \
+                 tests/jo.16.sh tests/jo.16.exp \
+                 tests/jo.17.sh tests/jo.17.exp tests/jo-creator.txt \
+                 tests/jo.18.sh tests/jo.18.exp \
+                 tests/jo.19.sh tests/jo.19.exp \
+                 tests/jo.20.sh tests/jo.20.exp \
+                 tests/jo.21.sh tests/jo.21.exp \
+                 tests/jo.22.sh tests/jo.22.exp \
+                 tests/jo.23.sh tests/jo.23.exp \
+                 tests/jo.24.sh tests/jo.24.exp \
+                 tests/jo.25.sh tests/jo.25.exp \
+                 tests/jo.26.sh tests/jo.26.exp \
+                 tests/jo.27.sh tests/jo.27.exp
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/README.md new/jo-1.6/README.md
--- old/jo-1.4/README.md        2020-07-18 18:20:38.000000000 +0200
+++ new/jo-1.6/README.md        2022-01-05 16:59:37.000000000 +0100
@@ -38,7 +38,7 @@
 
 ## Build from Github
 
-[![Build 
Status](https://api.travis-ci.org/jpmens/jo.svg?branch=master)](https://travis-ci.org/jpmens/jo)
+[![Build 
Status](https://api.travis-ci.com/jpmens/jo.svg?branch=master)](https://travis-ci.com/github/jpmens/jo)
 
 To install from the repository, you will need a C compiler as well as a 
relatively recent version of _automake_ and _autoconf_.
 
@@ -79,8 +79,10 @@
 * [ArchLinux](https://aur.archlinux.org/packages/jo/)
 * [OpenBSD](http://openports.se/textproc/jo)
 * [FreeBSD](https://www.freshports.org/textproc/jo)
+* [Guix](https://guix.gnu.org/packages/jo-1.4/)
 * [pkgsrc](http://pkgsrc.se/textproc/jo)
 * [repology.org](https://repology.org/metapackage/jo/versions)
+* [Docker](https://hub.docker.com/repository/docker/jpmens/jo)
 
 ## See also
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/configure.ac new/jo-1.6/configure.ac
--- old/jo-1.4/configure.ac     2020-07-18 18:20:38.000000000 +0200
+++ new/jo-1.6/configure.ac     2022-01-05 16:59:37.000000000 +0100
@@ -1,5 +1,5 @@
 AC_PREREQ([2.63])
-AC_INIT([jo], [1.4], [j...@mens.de])
+AC_INIT([jo], [1.6], [j...@mens.de])
 AC_CONFIG_AUX_DIR([build-aux])
 AC_CONFIG_SRCDIR([jo.c])
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/jo.1 new/jo-1.6/jo.1
--- old/jo-1.4/jo.1     2020-07-18 18:20:38.000000000 +0200
+++ new/jo-1.6/jo.1     2022-01-05 16:59:37.000000000 +0100
@@ -1,20 +1,28 @@
-.\"t
-.\" Automatically generated by Pandoc 2.5
+'\" t
+.\" Automatically generated by Pandoc 2.12
 .\"
 .TH "JO" "1" "" "User Manuals" ""
 .hy
 .SH NAME
 .PP
-jo \- JSON output from a shell
+jo - JSON output from a shell
 .SH SYNOPSIS
 .PP
-jo [\-p] [\-a] [\-B] [\-e] [\-v] [\-V] [\-d keydelim] [\[en]] [
-[\-s|\-n|\-b] word \&...]
+jo [-p] [-a] [-B] [-D] [-e] [-n] [-v] [-V] [-d keydelim] [-f file]
+[\[en]] [ [-s|-n|-b] word \&...]
 .SH DESCRIPTION
 .PP
-\f[I]jo\f[R] creates a JSON string on \f[I]stdout\f[R] from _word_s
-given it as arguments or read from \f[I]stdin\f[R].
-Without option \f[C]\-a\f[R] it generates an object whereby each
+\f[I]jo\f[R] creates a JSON string on \f[I]stdout\f[R] from
+\f[I]word\f[R]s given it as arguments or read from \f[I]stdin\f[R].
+If \f[C]-f\f[R] is specified, \f[I]jo\f[R] first loads the contents of
+\f[I]file\f[R] as a JSON object or array, then modifies it with
+subsequent \f[I]word\f[R]s before printing the final JSON string to
+\f[I]stdout\f[R].
+\f[I]file\f[R] may be specified as \f[C]-\f[R] to read from
+\f[I]jo\f[R]\[cq]s standard input; this takes precedence over reading
+\f[I]word\f[R]s from \f[I]stdin\f[R].
+.PP
+Without option \f[C]-a\f[R] it generates an object whereby each
 \f[I]word\f[R] is a \f[C]key=value\f[R] (or \f[C]key\[at]value\f[R])
 pair with \f[I]key\f[R] being the JSON object element and
 \f[I]value\f[R] its value.
@@ -22,8 +30,12 @@
 create number (using \f[I]strtod(3)\f[R]), string, or null values in
 JSON.
 .PP
+A missing or empty \f[I]value\f[R] normally results in an element whose
+value is \f[C]null\f[R].
+If \f[C]-n\f[R] is specified, this element is not created.
+.PP
 \f[I]jo\f[R] normally treats \f[I]key\f[R] as a literal string value.
-If the \f[C]\-d\f[R] option is specified, \f[I]key\f[R] will be
+If the \f[C]-d\f[R] option is specified, \f[I]key\f[R] will be
 interpreted as an \f[I]object path\f[R], whose individual components are
 separated by the first character of \f[I]keydelim\f[R].
 .PP
@@ -42,12 +54,12 @@
 T{
 \[at]file
 T}@T{
-substitute the contents of \f[I]file\f[R] as\-is
+substitute the contents of \f[I]file\f[R] as-is
 T}
 T{
 %file
 T}@T{
-substitute the contents of \f[I]file\f[R] in base64\-encoded form
+substitute the contents of \f[I]file\f[R] in base64-encoded form
 T}
 T{
 :file
@@ -64,25 +76,24 @@
 elements: if the value begins with \f[C]T\f[R], \f[C]t\f[R], or the
 numeric value is greater than zero, the result is \f[C]true\f[R], else
 \f[C]false\f[R].
-A missing or empty value behind the colon results in a \f[C]null\f[R]
-JSON element.
 .PP
-\f[I]jo\f[R] creates an array instead of an object when \f[C]\-a\f[R] is
+\f[I]jo\f[R] creates an array instead of an object when \f[C]-a\f[R] is
 specified.
 .PP
 When the \f[C]:=\f[R] operator is used in a \f[I]word\f[R], the name to
 the right of \f[C]:=\f[R] is a file containing JSON which is parsed and
 assigned to the key left of the operator.
-The file may be specified as \f[C]\-\f[R] to read from
-\f[I]jo\f[R]\[cq]s standard input.
+The file may be specified as \f[C]-\f[R] to read from \f[I]jo\f[R]\[cq]s
+standard input.
 .SH TYPE COERCION
 .PP
-\f[I]jo\f[R]\[cq]s type guesses can be overridden on a per\-word basis
-by prefixing \f[I]word\f[R] with \f[C]\-s\f[R] for \f[I]string\f[R],
-\f[C]\-n\f[R] for \f[I]number\f[R], or \f[C]\-b\f[R] for
+\f[I]jo\f[R]\[cq]s type guesses can be overridden on a per-word basis by
+prefixing \f[I]word\f[R] with \f[C]-s\f[R] for \f[I]string\f[R],
+\f[C]-n\f[R] for \f[I]number\f[R], or \f[C]-b\f[R] for
 \f[I]boolean\f[R].
-The list of _word_s \f[I]must\f[R] be prefixed with \f[C]\-\-\f[R], to
-indicate to \f[I]jo\f[R] that there are no more global options.
+The list of \f[I]word\f[R]s \f[I]must\f[R] be prefixed with
+\f[C]--\f[R], to indicate to \f[I]jo\f[R] that there are no more global
+options.
 .PP
 Type coercion works as follows:
 .PP
@@ -92,11 +103,11 @@
 T{
 word
 T}@T{
-\-s
+-s
 T}@T{
-\-n
+-n
 T}@T{
-\-b
+-b
 T}@T{
 default
 T}
@@ -180,19 +191,19 @@
 T}
 .TE
 .PP
-Coercing a non\-number string to number outputs the \f[I]length\f[R] of
+Coercing a non-number string to number outputs the \f[I]length\f[R] of
 the string.
 .PP
-Coercing a non\-boolean string to boolean outputs \f[C]false\f[R] if the
+Coercing a non-boolean string to boolean outputs \f[C]false\f[R] if the
 string is empty, \f[C]true\f[R] otherwise.
 .PP
 Type coercion only applies to \f[C]key=value\f[R] words, and individual
-words in a \f[C]\-a\f[R] array.
+words in a \f[C]-a\f[R] array.
 Coercing other words has no effect.
 .SH EXAMPLES
 .PP
 Create an object.
-Note how the incorrectly\-formatted float value becomes a string:
+Note how the incorrectly-formatted float value becomes a string:
 .IP
 .nf
 \f[C]
@@ -201,11 +212,11 @@
 \f[R]
 .fi
 .PP
-Pretty\-print an array with a list of files in the current directory:
+Pretty-print an array with a list of files in the current directory:
 .IP
 .nf
 \f[C]
-$ jo \-p \-a *
+$ jo -p -a *
 [
  \[dq]Makefile\[dq],
  \[dq]README.md\[dq],
@@ -225,7 +236,7 @@
 .IP
 .nf
 \f[C]
-$ jo \-p name=JP object=$(jo fruit=Orange hungry\[at]0 point=$(jo x=10 y=20 
list=$(jo \-a 1 2 3 4 5)) number=17) sunday\[at]0
+$ jo -p name=JP object=$(jo fruit=Orange hungry\[at]0 point=$(jo x=10 y=20 
list=$(jo -a 1 2 3 4 5)) number=17) sunday\[at]0
 {
  \[dq]name\[dq]: \[dq]JP\[dq],
  \[dq]object\[dq]: {
@@ -250,16 +261,16 @@
 .fi
 .PP
 Booleans as strings or as boolean (pay particular attention to
-\f[I]switch\f[R]; the \f[C]\-B\f[R] option disables the default
-detection of the \[lq]\f[C]true\f[R]\[rq], \[lq]\f[C]false\f[R]\[rq],
-and \[lq]\f[C]null\f[R]\[rq] strings):
+\f[I]switch\f[R]; the \f[C]-B\f[R] option disables the default detection
+of the \[lq]\f[C]true\f[R]\[rq], \[lq]\f[C]false\f[R]\[rq], and
+\[lq]\f[C]null\f[R]\[rq] strings):
 .IP
 .nf
 \f[C]
 $ jo switch=true morning\[at]0
 {\[dq]switch\[dq]:true,\[dq]morning\[dq]:false}
 
-$ jo \-B switch=true morning\[at]0
+$ jo -B switch=true morning\[at]0
 {\[dq]switch\[dq]:\[dq]true\[dq],\[dq]morning\[dq]:false}
 \f[R]
 .fi
@@ -270,7 +281,7 @@
 .IP
 .nf
 \f[C]
-$ jo \-p name=Jane point[]=1 point[]=2 geo[lat]=10 geo[lon]=20
+$ jo -p name=Jane point[]=1 point[]=2 geo[lat]=10 geo[lon]=20
 {
    \[dq]name\[dq]: \[dq]Jane\[dq],
    \[dq]point\[dq]: [
@@ -289,7 +300,7 @@
 .IP
 .nf
 \f[C]
-$ jo \-p \-d. name=Jane point[]=1 point[]=2 geo.lat=10 geo.lon=20
+$ jo -p -d. name=Jane point[]=1 point[]=2 geo.lat=10 geo.lon=20
 {
    \[dq]name\[dq]: \[dq]Jane\[dq],
    \[dq]point\[dq]: [
@@ -304,11 +315,11 @@
 \f[R]
 .fi
 .PP
-Without \f[C]\-d\f[R], a different object is generated:
+Without \f[C]-d\f[R], a different object is generated:
 .IP
 .nf
 \f[C]
-$ jo \-p name=Jane point[]=1 point[]=2 geo.lat=10 geo.lon=20
+$ jo -p name=Jane point[]=1 point[]=2 geo.lat=10 geo.lon=20
 {
    \[dq]name\[dq]: \[dq]Jane\[dq],
    \[dq]point\[dq]: [
@@ -329,7 +340,7 @@
 {}
 
 $ MY_ARRAY=(a=1 b=2)
-$ jo \-a \[dq]${MY_ARRAY[\[at]]}\[dq] < /dev/null
+$ jo -a \[dq]${MY_ARRAY[\[at]]}\[dq] < /dev/null
 [\[dq]a=1\[dq],\[dq]b=2\[dq]]
 \f[R]
 .fi
@@ -338,7 +349,7 @@
 .IP
 .nf
 \f[C]
-$ jo \-p \-\- \-s a=true b=true \-s c=123 d=123 \-b e=\[dq]1\[dq] \-b 
f=\[dq]true\[dq] \-n g=\[dq]This is a test\[dq] \-b h=\[dq]This is a test\[dq]
+$ jo -p -- -s a=true b=true -s c=123 d=123 -b e=\[dq]1\[dq] -b 
f=\[dq]true\[dq] -n g=\[dq]This is a test\[dq] -b h=\[dq]This is a test\[dq]
 {
    \[dq]a\[dq]: \[dq]true\[dq],
    \[dq]b\[dq]: true,
@@ -350,20 +361,20 @@
    \[dq]h\[dq]: true
 }
 
-$ jo \-a \-\- \-s 123 \-n \[dq]This is a test\[dq] \-b C_Rocks 456
+$ jo -a -- -s 123 -n \[dq]This is a test\[dq] -b C_Rocks 456
 [\[dq]123\[dq],14,true,456]
 \f[R]
 .fi
 .PP
 Read element values from files: a value which starts with
 \f[C]\[at]\f[R] is read in plain whereas if it begins with a \f[C]%\f[R]
-it will be base64\-encoded and if it starts with \f[C]:\f[R] the
-contents are interpreted as JSON:
+it will be base64-encoded and if it starts with \f[C]:\f[R] the contents
+are interpreted as JSON:
 .IP
 .nf
 \f[C]
 $ jo program=jo authors=\[at]AUTHORS
-{\[dq]program\[dq]:\[dq]jo\[dq],\[dq]authors\[dq]:\[dq]Jan\-Piet Mens 
<jpmens\[at]gmail.com>\[dq]}
+{\[dq]program\[dq]:\[dq]jo\[dq],\[dq]authors\[dq]:\[dq]Jan-Piet Mens 
<jpmens\[at]gmail.com>\[dq]}
 
 $ jo filename=AUTHORS content=%AUTHORS
 
{\[dq]filename\[dq]:\[dq]AUTHORS\[dq],\[dq]content\[dq]:\[dq]SmFuLVBpZXQgTWVucyA8anBtZW5zQGdtYWlsLmNvbT4K\[dq]}
@@ -393,42 +404,72 @@
 .IP
 .nf
 \f[C]
-$ ls | jo \-a > child.json
+$ ls | jo -a > child.json
 $ jo files:=child.json
 {\[dq]files\[dq]:[\[dq]AUTHORS\[dq],\[dq]COPYING\[dq],\[dq]ChangeLog\[dq] ....
 
-$ ls *.c | jo \-a > source.json; ls *.h | jo \-a > headers.json
-$ jo \-a :source.json :headers.json
+$ ls *.c | jo -a > source.json; ls *.h | jo -a > headers.json
+$ jo -a :source.json :headers.json
 
[[\[dq]base64.c\[dq],\[dq]jo.c\[dq],\[dq]json.c\[dq]],[\[dq]base64.h\[dq],\[dq]json.h\[dq]]]
 \f[R]
 .fi
+.PP
+Add elements to existing JSON:
+.IP
+.nf
+\f[C]
+$ jo -f source.json 1 | jo -f - 2 3
+[\[dq]base64.c\[dq],\[dq]jo.c\[dq],\[dq]json.c\[dq],1,2,3]
+
+$ curl -s 
\[aq]https://noembed.com/embed?url=https://www.youtube.com/watch?v=dQw4w9WgXcQ\[aq]
 | jo -f - status=Rickrolled
+{ ...., 
\[dq]type\[dq]:\[dq]video\[dq],\[dq]author_url\[dq]:\[dq]https://www.youtube.com/user/RickAstleyVEVO\[dq],\[dq]status\[dq]:\[dq]Rickrolled\[dq]}
+\f[R]
+.fi
+.PP
+Deduplicate object keys (\f[I]jo\f[R] appends duplicate object keys by
+default):
+.IP
+.nf
+\f[C]
+$ jo a=1 b=2 a=3
+{\[dq]a\[dq]:1,\[dq]b\[dq]:2,\[dq]a\[dq]:3}
+$ jo -D a=1 b=2 a=3
+{\[dq]a\[dq]:3,\[dq]b\[dq]:2}
+\f[R]
+.fi
 .SH OPTIONS
 .PP
 \f[I]jo\f[R] understands the following global options.
 .TP
-.B \-a
+-a
 Interpret the list of \f[I]words\f[R] as array values and produce an
 array instead of an object.
 .TP
-.B \-B
-By default \f[I]jo\f[R] interprets the strings \[lq]\f[C]true\f[R]\[rq]
+-B
+By default, \f[I]jo\f[R] interprets the strings \[lq]\f[C]true\f[R]\[rq]
 and \[lq]\f[C]false\f[R]\[rq] as boolean elements \f[C]true\f[R] and
 \f[C]false\f[R] respectively, and \[lq]\f[C]null\f[R]\[rq] as
 \f[C]null\f[R].
 Disable with this option.
 .TP
-.B \-e
+-D
+Deduplicate object keys.
+.TP
+-e
 Ignore empty stdin (i.e.\ don\[cq]t produce a diagnostic error when
 \f[I]stdin\f[R] is empty)
 .TP
-.B \-p
-Pretty\-print the JSON string on output instead of the terse one\-line
+-n
+Do not add keys with empty values.
+.TP
+-p
+Pretty-print the JSON string on output instead of the terse one-line
 output it prints by default.
 .TP
-.B \-v
+-v
 Show version and exit.
 .TP
-.B \-V
+-V
 Show version as a JSON object and exit.
 .SH BUGS
 .PP
@@ -459,7 +500,7 @@
 This was designed thusly.
 .SH RETURN CODES
 .PP
-\f[I]jo\f[R] exits with a code 0 on success and non\-zero on failure
+\f[I]jo\f[R] exits with a code 0 on success and non-zero on failure
 after indicating what caused the failure.
 .SH AVAILABILITY
 .PP
@@ -479,4 +520,4 @@
 strtod(3)
 .SH AUTHOR
 .PP
-Jan\-Piet Mens <http://jpmens.net>
+Jan-Piet Mens <http://jpmens.net>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/jo.c new/jo-1.6/jo.c
--- old/jo-1.4/jo.c     2020-07-18 18:20:38.000000000 +0200
+++ new/jo-1.6/jo.c     2022-01-05 16:59:37.000000000 +0100
@@ -36,7 +36,8 @@
 #define FLAG_NOBOOL    0x04
 #define FLAG_BOOLEAN   0x08
 #define FLAG_NOSTDIN   0x10
-#define FLAG_MASK      (FLAG_ARRAY | FLAG_PRETTY | FLAG_NOBOOL | FLAG_BOOLEAN 
| FLAG_NOSTDIN)
+#define FLAG_SKIPNULLS 0x20
+#define FLAG_MASK      (FLAG_ARRAY | FLAG_PRETTY | FLAG_NOBOOL | FLAG_BOOLEAN 
| FLAG_NOSTDIN | FLAG_SKIPNULLS)
 
 /* Size of buffer blocks for pipe slurping */
 #define SLURP_BLOCK_SIZE 4096
@@ -100,12 +101,6 @@
        }
 }
 
-const char *maybe_stdin(const char* filename)
-{
-       return strcmp(filename, "-") ? filename : "/dev/fd/0";
-}
-               
-
 char *slurp_file(const char* filename, size_t *out_len, bool fold_newlines)
 {
        char *buf;
@@ -113,8 +108,10 @@
        int ch;
        off_t buffer_len;
        FILE *fp;
+       bool use_stdin = strcmp(filename, "-") == 0;
 
-       if ((fp = fopen(maybe_stdin(filename), "r")) == NULL) {
+       if (use_stdin) fp = stdin;
+       else if ((fp = fopen(filename, "r")) == NULL) {
                perror(filename);
                errx(1, "Cannot open %s for reading", filename);
        }
@@ -142,7 +139,7 @@
                        buf[i++] = ch;
                }
        }
-        fclose(fp);
+       if (!use_stdin) fclose(fp);
        buf[i] = 0;
        *out_len = i;
        return (buf);
@@ -224,7 +221,7 @@
        JsonTag type = flags_to_tag(flags);
 
        if (strlen(str) == 0) {
-               return jo_mknull(type);
+               return (flags & FLAG_SKIPNULLS) ? (JsonNode *)NULL : 
jo_mknull(type);
        }
 
        /* If str begins with a double quote, keep it a string */
@@ -339,13 +336,15 @@
 
 int usage(char *prog)
 {
-       fprintf(stderr, "Usage: %s [-a] [-B] [-d keydelim] [-p] [-e] [-v] [-V] 
[--] [-s|-n|-b] [word...]\n", prog);
+       fprintf(stderr, "Usage: %s [-a] [-B] [-D] [-d keydelim] [-p] [-e] [-n] 
[-v] [-V] [-f file] [--] [-s|-n|-b] [word...]\n", prog);
        fprintf(stderr, "\tword is key=value or key@value\n");
        fprintf(stderr, "\t-a creates an array of words\n");
-       fprintf(stderr, "\t-B disable boolean true/false\n");
+       fprintf(stderr, "\t-B disable boolean true/false/null detection\n");
+       fprintf(stderr, "\t-D deduplicate object keys\n");
        fprintf(stderr, "\t-d key will be object path separated by keydelim\n");
+       fprintf(stderr, "\t-f load file as JSON object or array\n");
        fprintf(stderr, "\t-p pretty-prints JSON on output\n");
-       fprintf(stderr, "\t-e if stdin is empty do not wait for input quit\n");
+       fprintf(stderr, "\t-e quit if stdin is empty do not wait for input\n");
        fprintf(stderr, "\t-s coerce type guessing to string\n");
        fprintf(stderr, "\t-b coerce type guessing to bool\n");
        fprintf(stderr, "\t-n coerce type guessing to number\n");
@@ -365,12 +364,14 @@
  *   *baseop -> object node in which caller should insert "value"
  */
 
-bool resolve_nested(int flags, char **keyp, char key_delim, char *value, 
JsonNode **baseop)
+bool resolve_nested(int flags, char **keyp, char key_delim, JsonNode *value, 
JsonNode **baseop)
 {
        char *member = NULL, *bo, *bc, *so;             /* bracket open, close, 
sub-object */
        JsonNode *op;
        int found = false;
 
+       (void)flags;
+
        if (key_delim) {
                /* First construct nested object */
                while ((so = strchr(*keyp, key_delim)) != NULL) {
@@ -411,17 +412,9 @@
                }
 
                if (member == NULL) {           /* we're doing an array */
-                       if (flags & FLAG_BOOLEAN) {
-                               json_append_element(op, boolnode(value));
-                       } else {
-                               json_append_element(op, vnode(value, flags));
-                       }
+                       json_append_element(op, value);
                } else {                        /* we're doing an object */
-                       if (flags & FLAG_BOOLEAN) {
-                               json_append_member(op, member, boolnode(value));
-                       } else {
-                               json_append_member(op, member, vnode(value, 
flags));
-                       }
+                       json_append_member(op, member, value);
                }
 
                if (!found) {
@@ -457,7 +450,8 @@
                }
 
                *r = 0;         /* Chop at ":=" */
-               json_append_member(object, kv, o);
+               if (!resolve_nested(flags, &kv, key_delim, o, &object))
+                       json_append_member(object, kv, o);
                return (0);
        }
 
@@ -465,20 +459,20 @@
                return (-1);
        }
 
-
+       JsonNode *val;
        if (p) {
-               if (p) {
-                       *p = 0;
+               *p = 0;
+               val = vnode(p+1, flags);
 
-                       if (!resolve_nested(flags, &kv, key_delim, p+1, 
&object))
-                               json_append_member(object, kv, vnode(p+1, 
flags));
-               }
+               if (!resolve_nested(flags, &kv, key_delim, val, &object))
+                       json_append_member(object, kv, val);
        } else {
                if (q) {
                        *q = 0;
+                       val = boolnode(q+1);
 
-                       if (!resolve_nested(flags | FLAG_BOOLEAN, &kv, 
key_delim, q+1, &object))
-                               json_append_member(object, kv, boolnode(q+1));
+                       if (!resolve_nested(flags | FLAG_BOOLEAN, &kv, 
key_delim, val, &object))
+                               json_append_member(object, kv, val);
                }
        }
        return (0);
@@ -567,9 +561,9 @@
 # define locale_free(p) free(p)
 #else
 # define utf8_from_locale(p, l) (p)
-# define utf8_free(p)
+# define utf8_free(p) do {} while (0)
 # define locale_from_utf8(p, l) (p)
-# define locale_free(p)
+# define locale_free(p) do {} while (0)
 #endif
 
 char *stringify(JsonNode *json, int flags)
@@ -602,6 +596,8 @@
        int c, key_delim = 0;
        bool showversion = false;
        char *kv, *js_string, *progname, buf[BUFSIZ], *p;
+       char *in_file = NULL, *in_str;
+       size_t in_len = 0;
        int ttyin = isatty(fileno(stdin)), ttyout = isatty(fileno(stdout));
        int flags = 0;
        JsonNode *json, *op;
@@ -614,7 +610,7 @@
 
        progname = (progname = strrchr(*argv, '/')) ? progname + 1 : *argv;
 
-       while ((c = getopt(argc, argv, "aBd:hpevV")) != EOF) {
+       while ((c = getopt(argc, argv, "aBDd:f:hpenvV")) != EOF) {
                switch (c) {
                        case 'a':
                                flags |= FLAG_ARRAY;
@@ -622,9 +618,15 @@
                        case 'B':
                                flags |= FLAG_NOBOOL;
                                break;
+                       case 'D':
+                               json_dedup_members(true);
+                               break;
                        case 'd':
                                key_delim = optarg[0];
                                break;
+                       case 'f':
+                               in_file = optarg;
+                               break;
                        case 'h':
                                usage(progname);
                                return (0);
@@ -634,6 +636,9 @@
                        case 'e':
                                flags |= FLAG_NOSTDIN;
                                break;
+                       case 'n':
+                               flags |= FLAG_SKIPNULLS;
+                               break;
                        case 'v':
                                printf("jo %s\n", PACKAGE_VERSION);
                                exit(0);
@@ -653,7 +658,26 @@
        argv += optind;
 
        pile = json_mkobject();
-       json = (flags & FLAG_ARRAY) ? json_mkarray() : json_mkobject();
+       if (in_file != NULL) {
+               if ((in_str = slurp_file(in_file, &in_len, false)) == NULL) {
+                       errx(1, "Error reading file %s", in_file);
+               }
+               json = json_decode(in_str);
+               if (json) {
+                       switch (json->tag) {
+                               case JSON_ARRAY:
+                                       flags |= FLAG_ARRAY;
+                                       break;
+                               case JSON_OBJECT:
+                                       break;
+                               default:
+                                       errx(1, "Input JSON not an array or 
object: %s", stringify(json, flags));
+                       }
+               } else
+                       json = (flags & FLAG_ARRAY) ? json_mkarray() : 
json_mkobject();
+       } else {
+               json = (flags & FLAG_ARRAY) ? json_mkarray() : json_mkobject();
+       }
 
        if (argc == 0) {
                if (flags & FLAG_NOSTDIN) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/jo.md new/jo-1.6/jo.md
--- old/jo-1.4/jo.md    2020-07-18 18:20:38.000000000 +0200
+++ new/jo-1.6/jo.md    2022-01-05 16:59:37.000000000 +0100
@@ -1,5 +1,5 @@
 ---
-title: 'JO(1) User Manuals'
+title: JO(1) User Manuals
 ---
 
 NAME
@@ -10,18 +10,27 @@
 SYNOPSIS
 ========
 
-jo \[-p\] \[-a\] \[-B\] \[-e\] \[-v\] \[-V\] \[-d keydelim\] \[--\] \[
-\[-s\|-n\|-b\] word ...\]
+jo \[-p\] \[-a\] \[-B\] \[-D\] \[-e\] \[-n\] \[-v\] \[-V\] \[-d
+keydelim\] \[-f file\] \[--\] \[ \[-s\|-n\|-b\] word ...\]
 
 DESCRIPTION
 ===========
 
-*jo* creates a JSON string on *stdout* from \_word\_s given it as
-arguments or read from *stdin*. Without option `-a` it generates an
-object whereby each *word* is a `key=value` (or `key@value`) pair with
-*key* being the JSON object element and *value* its value. *jo* attempts
-to guess the type of *value* in order to create number (using
-*strtod(3)*), string, or null values in JSON.
+*jo* creates a JSON string on *stdout* from *word*s given it as
+arguments or read from *stdin*. If `-f` is specified, *jo* first loads
+the contents of *file* as a JSON object or array, then modifies it with
+subsequent *word*s before printing the final JSON string to *stdout*.
+*file* may be specified as `-` to read from *jo*'s standard input; this
+takes precedence over reading *word*s from *stdin*.
+
+Without option `-a` it generates an object whereby each *word* is a
+`key=value` (or `key@value`) pair with *key* being the JSON object
+element and *value* its value. *jo* attempts to guess the type of
+*value* in order to create number (using *strtod(3)*), string, or null
+values in JSON.
+
+A missing or empty *value* normally results in an element whose value is
+`null`. If `-n` is specified, this element is not created.
 
 *jo* normally treats *key* as a literal string value. If the `-d` option
 is specified, *key* will be interpreted as an *object path*, whose
@@ -31,19 +40,18 @@
 *jo* normally treats *value* as a literal string value, unless it begins
 with one of the following characters:
 
-  value    action
-  -------- 
---------------------------------------------------------------------
-  @file    substitute the contents of *file* as-is
-  \%file   substitute the contents of *file* in base64-encoded form
-  :file    interpret the contents of *file* as JSON, and substitute the result
+  value   action
+  ------- ---------------------------------------------------------------------
+  @file   substitute the contents of *file* as-is
+  %file   substitute the contents of *file* in base64-encoded form
+  :file   interpret the contents of *file* as JSON, and substitute the result
 
 Escape the special character with a backslash to prevent this
 interpretation.
 
 *jo* treats `key@value` specifically as boolean JSON elements: if the
 value begins with `T`, `t`, or the numeric value is greater than zero,
-the result is `true`, else `false`. A missing or empty value behind the
-colon results in a `null` JSON element.
+the result is `true`, else `false`.
 
 *jo* creates an array instead of an object when `-a` is specified.
 
@@ -57,7 +65,7 @@
 
 *jo*'s type guesses can be overridden on a per-word basis by prefixing
 *word* with `-s` for *string*, `-n` for *number*, or `-b` for *boolean*.
-The list of \_word\_s *must* be prefixed with `--`, to indicate to *jo*
+The list of *word*s *must* be prefixed with `--`, to indicate to *jo*
 that there are no more global options.
 
 Type coercion works as follows:
@@ -233,7 +241,7 @@
     $ jo action="split window" vimcmd="\:split"
     {"action":"split window","vimcmd":":split"}
 
-Read element values from a file in order to overcome ARG\_MAX limits
+Read element values from a file in order to overcome ARG_MAX limits
 during object assignment:
 
     $ ls | jo -a > child.json
@@ -244,6 +252,21 @@
     $ jo -a :source.json :headers.json
     [["base64.c","jo.c","json.c"],["base64.h","json.h"]]
 
+Add elements to existing JSON:
+
+    $ jo -f source.json 1 | jo -f - 2 3
+    ["base64.c","jo.c","json.c",1,2,3]
+
+    $ curl -s 
'https://noembed.com/embed?url=https://www.youtube.com/watch?v=dQw4w9WgXcQ' | 
jo -f - status=Rickrolled
+    { ...., 
"type":"video","author_url":"https://www.youtube.com/user/RickAstleyVEVO","status":"Rickrolled"}
+
+Deduplicate object keys (*jo* appends duplicate object keys by default):
+
+    $ jo a=1 b=2 a=3
+    {"a":1,"b":2,"a":3}
+    $ jo -D a=1 b=2 a=3
+    {"a":3,"b":2}
+
 OPTIONS
 =======
 
@@ -254,14 +277,20 @@
     instead of an object.
 
 -B
-:   By default *jo* interprets the strings "`true`" and "`false`" as
+:   By default, *jo* interprets the strings "`true`" and "`false`" as
     boolean elements `true` and `false` respectively, and "`null`" as
     `null`. Disable with this option.
 
+-D
+:   Deduplicate object keys.
+
 -e
 :   Ignore empty stdin (i.e.??don't produce a diagnostic error when
     *stdin* is empty)
 
+-n
+:   Do not add keys with empty values.
+
 -p
 :   Pretty-print the JSON string on output instead of the terse one-line
     output it prints by default.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/jo.pandoc new/jo-1.6/jo.pandoc
--- old/jo-1.4/jo.pandoc        2020-07-18 18:20:38.000000000 +0200
+++ new/jo-1.6/jo.pandoc        2022-01-05 16:59:37.000000000 +0100
@@ -6,15 +6,23 @@
 
 # SYNOPSIS
 
-jo [-p] [-a] [-B] [-e] [-v] [-V] [-d keydelim] [--] [ [-s|-n|-b] word ...]
+jo [-p] [-a] [-B] [-D] [-e] [-n] [-v] [-V] [-d keydelim] [-f file] [--] [ 
[-s|-n|-b] word ...]
 
 # DESCRIPTION
 
-*jo* creates a JSON string on _stdout_ from _word_s given it as arguments or 
read from _stdin_. Without
-option `-a` it generates an object whereby each _word_ is a `key=value` (or 
`key@value`)
+*jo* creates a JSON string on _stdout_ from *word*s given it as arguments or 
read from _stdin_.
+If `-f` is specified, *jo* first loads the contents of _file_ as a JSON object 
or array, then
+modifies it with subsequent *word*s before printing the final JSON string to 
_stdout_.
+_file_ may be specified as `-` to read from _jo_'s standard input; this takes 
precedence over
+reading *word*s from _stdin_.
+
+Without option `-a` it generates an object whereby each _word_ is a 
`key=value` (or `key@value`)
 pair with _key_ being the JSON object element and _value_ its value. *jo* 
attempts to
 guess the type of _value_ in order to create number (using _strtod(3)_), 
string, or null values in JSON.
 
+A missing or empty _value_ normally results in an element whose value is 
`null`. If `-n` is specified, this
+element is not created.
+
 *jo* normally treats _key_ as a literal string value. If the `-d` option is 
specified, _key_ will be
 interpreted as an _object path_, whose individual components are separated by 
the first character of _keydelim_.
 
@@ -29,8 +37,7 @@
 Escape the special character with a backslash to prevent this interpretation.
 
 *jo* treats `key@value` specifically as boolean JSON elements: if the value 
begins with `T`, `t`,
-or the numeric value is greater than zero, the result is `true`, else `false`. 
A missing or
-empty value behind the colon results in a `null` JSON element.
+or the numeric value is greater than zero, the result is `true`, else `false`.
 
 *jo* creates an array instead of an object when `-a` is specified.
 
@@ -40,7 +47,7 @@
 # TYPE COERCION
 
 *jo*'s type guesses can be overridden on a per-word basis by prefixing _word_ 
with `-s` for _string_,
-`-n` for _number_, or `-b` for _boolean_. The list of _word_s *must* be 
prefixed with `--`, to indicate
+`-n` for _number_, or `-b` for _boolean_. The list of *word*s *must* be 
prefixed with `--`, to indicate
 to *jo* that there are no more global options.
 
 Type coercion works as follows:
@@ -216,22 +223,43 @@
        $ jo -a :source.json :headers.json
        [["base64.c","jo.c","json.c"],["base64.h","json.h"]]
 
+Add elements to existing JSON:
+
+       $ jo -f source.json 1 | jo -f - 2 3
+       ["base64.c","jo.c","json.c",1,2,3]
+
+       $ curl -s 
'https://noembed.com/embed?url=https://www.youtube.com/watch?v=dQw4w9WgXcQ' | 
jo -f - status=Rickrolled
+       { ...., 
"type":"video","author_url":"https://www.youtube.com/user/RickAstleyVEVO","status":"Rickrolled"}
+
+Deduplicate object keys (*jo* appends duplicate object keys by default):
+
+       $ jo a=1 b=2 a=3
+       {"a":1,"b":2,"a":3}
+       $ jo -D a=1 b=2 a=3
+       {"a":3,"b":2}
+
 # OPTIONS
 
-*jo* understands the following global options. 
+*jo* understands the following global options.
 
 -a
 :   Interpret the list of _words_ as array values and produce an array instead 
of
     an object.
 
 -B
-:   By default *jo* interprets the strings "`true`" and "`false`" as boolean 
elements
+:   By default, *jo* interprets the strings "`true`" and "`false`" as boolean 
elements
     `true` and `false` respectively, and "`null`" as `null`. Disable with this 
option.
 
+-D
+:   Deduplicate object keys.
+
 -e
 :   Ignore empty stdin (i.e. don't produce a diagnostic error when *stdin*
     is empty)
 
+-n
+:   Do not add keys with empty values.
+
 -p
 :   Pretty-print the JSON string on output instead of the terse one-line 
output it
     prints by default.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/jo.zsh new/jo-1.6/jo.zsh
--- old/jo-1.4/jo.zsh   1970-01-01 01:00:00.000000000 +0100
+++ new/jo-1.6/jo.zsh   2022-01-05 16:59:37.000000000 +0100
@@ -0,0 +1,62 @@
+#compdef jo
+
+# Completion function for zsh
+# Store this file in a directory listed in $fpath for it to be picked up
+# by compinit. It needs to be named with an initial underscore, e.g. _jo
+
+local curcontext="$curcontext"
+local -i aopt nm=$compstate[nmatches]
+local -a expl line state state_descr
+local -A opt_args
+
+_arguments -C -s -A "-*" \
+  '(-h)-p[pretty-print JSON on output]' \
+  '(-d -h)-a[create an array of words]' \
+  '(-v -V -h)-B[disable interpretation of true/false/null strings]' \
+  "(-v -V -h)-e[if stdin is empty don't wait for input - quit]" \
+  '(- *)-v[show version information]' \
+  '(-a -B -e -h -v *)-V[show version in JSON]' \
+  '(-a -h -v -V)-d+[key will be object path separated by given delimiter]:key 
delimiter' \
+  '(- *)-h[show usage information]' \
+  '*::word:->words'
+
+if [[ -n $state ]]; then
+  aopt=$+opt_args[-a]
+  _arguments \
+    '*-s[coerce type guessing to string]: :->words' \
+    '*-b[coerce type guessing to bool]: :->words' \
+    '*-n[coerce type guessing to number]: :->words' \
+    '*: :->words'
+
+  if (( aopt )); then
+    _message -e words 'array element'
+  elif compset -P 1 '*:='; then
+    _alternative 'files:file:_files' 'operators:stdin:(-)'
+  elif compset -P 1 '*='; then
+    if compset -P '[@%:]'; then
+      _files
+    else
+      _describe -t operators "file prefix" '(
+        @:substitute\ file\ as-is
+        %:substitute\ file\ in\ base64-encoded\ form
+        \\::substitute\ file\ as\ JSON
+      )' -S ''
+      _message -e values value
+    fi
+  elif compset -P 1 '?*@'; then
+    _description booleans expl 'boolean'
+    compadd -M 'm:{a-zA-Z}={A-Za-z} m:{10}={TF}' "$expl[@]" True False
+  else
+    if compset -P '[^-]*'; then
+      _describe -t suffixes suffix '(
+        @:boolean\ element
+        \=:value
+        \\:=:substitute\ JSON\ file
+        \[\]:array\ element
+      )' -S ''
+    fi
+    _message -e keys key
+  fi
+fi
+
+[[ nm -ne compstate[nmatches] ]]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/json.c new/jo-1.6/json.c
--- old/jo-1.4/json.c   2020-07-18 18:20:38.000000000 +0200
+++ new/jo-1.6/json.c   2022-01-05 16:59:37.000000000 +0100
@@ -34,6 +34,18 @@
                exit(EXIT_FAILURE);                     \
        } while (0)
 
+#ifdef _WIN32
+# define failx(e, n, f, ...)   if (!(e)) {     \
+               fprintf(stderr, "JSON_ERR: " f, __VA_ARGS__);   \
+               exit(n);        \
+       }
+#else
+# include <err.h>
+# define failx(e, n, f, ...)   if (!(e)) {     \
+               errx(n, "JSON_ERR: " f, __VA_ARGS__);   \
+       }
+#endif
+
 /* Sadly, strdup is not portable. */
 static char *json_strdup(const char *str)
 {
@@ -271,7 +283,12 @@
 {
        unsigned char *o = (unsigned char*) out;
        
-       assert(unicode <= 0x10FFFF && !(unicode >= 0xD800 && unicode <= 
0xDFFF));
+       failx(
+               unicode <= 0x10FFFF && !(unicode >= 0xD800 && unicode <= 
0xDFFF),
+               1,
+               "Illegal Unicode codepoint 0x%08X found",
+               unicode
+       );
 
        if (unicode <= 0x7F) {
                /* U+0000..U+007F */
@@ -323,7 +340,12 @@
 {
        js_uchar_t n;
        
-       assert(unicode >= 0x10000 && unicode <= 0x10FFFF);
+       failx(
+               unicode >= 0x10000 && unicode <= 0x10FFFF,
+               1,
+               "Cannot construct UTF-16 surrogate pair for Unicode codepoint 
0x%08X",
+               unicode
+       );
        
        n = unicode - 0x10000;
        *uc = ((n >> 10) & 0x3FF) | 0xD800;
@@ -357,8 +379,11 @@
 static JsonNode *mknode(JsonTag tag);
 static void append_node(JsonNode *parent, JsonNode *child);
 static void prepend_node(JsonNode *parent, JsonNode *child);
+static void insert_node(JsonNode *parent, JsonNode *child);
 static void append_member(JsonNode *object, char *key, JsonNode *value);
 
+static void (*append_member_node_fn)(JsonNode *parent, JsonNode *child) = 
append_node;
+
 /* Assertion-friendly validity checks */
 static bool tag_is_valid(unsigned int tag);
 static bool number_is_valid(const char *num);
@@ -540,6 +565,7 @@
 
 static void append_node(JsonNode *parent, JsonNode *child)
 {
+       if (!child) return;
        child->parent = parent;
        child->prev = parent->children.tail;
        child->next = NULL;
@@ -553,6 +579,7 @@
 
 static void prepend_node(JsonNode *parent, JsonNode *child)
 {
+       if (!child) return;
        child->parent = parent;
        child->prev = NULL;
        child->next = parent->children.head;
@@ -564,15 +591,52 @@
        parent->children.head = child;
 }
 
+static void insert_node(JsonNode *parent, JsonNode *child)
+{
+       if (!child) return;
+       JsonNode *this = parent->children.head;
+
+       while (this != NULL && strcmp(this->key, child->key))
+               this = this->next;
+
+       if (this != NULL)
+       {
+               /* we found a matching key, insert before this node */
+               if (this->prev == NULL)
+               {
+                       prepend_node(parent, child);
+               }
+               else
+               {
+                       child->parent = parent;
+                       child->next = this->next;
+                       child->prev = this->prev;
+                       this->prev->next = child;
+                       this->prev = child;
+               }
+               json_delete(this);
+       }
+       else
+               append_node(parent, child);
+}
+
 static void append_member(JsonNode *object, char *key, JsonNode *value)
 {
+       if (!value) return;
        value->key = key;
-       append_node(object, value);
+       append_member_node_fn(object, value);
 }
 
 void json_append_element(JsonNode *array, JsonNode *element)
 {
-       assert(array->tag == JSON_ARRAY);
+       if (!element) return;
+       failx(
+               array->tag == JSON_ARRAY,
+               1,
+               "Cannot append %s to non-array %s",
+               json_encode(element),
+               json_encode(array)
+       );
        assert(element->parent == NULL);
        
        append_node(array, element);
@@ -580,7 +644,14 @@
 
 void json_prepend_element(JsonNode *array, JsonNode *element)
 {
-       assert(array->tag == JSON_ARRAY);
+       if (!element) return;
+       failx(
+               array->tag == JSON_ARRAY,
+               1,
+               "Cannot append %s to non-array %s",
+               json_encode(element),
+               json_encode(array)
+       );
        assert(element->parent == NULL);
        
        prepend_node(array, element);
@@ -588,7 +659,15 @@
 
 void json_append_member(JsonNode *object, const char *key, JsonNode *value)
 {
-       assert(object->tag == JSON_OBJECT);
+       if (!value) return;
+       failx(
+               object->tag == JSON_OBJECT,
+               1,
+               "Cannot add {\"%s\":%s} to non-object %s",
+               key,
+               json_encode(value),
+               json_encode(object)
+       );
        assert(value->parent == NULL);
        
        append_member(object, json_strdup(key), value);
@@ -596,13 +675,26 @@
 
 void json_prepend_member(JsonNode *object, const char *key, JsonNode *value)
 {
-       assert(object->tag == JSON_OBJECT);
+       if (!value) return;
+       failx(
+               object->tag == JSON_OBJECT,
+               1,
+               "Cannot add {\"%s\":%s} to non-object %s",
+               key,
+               json_encode(value),
+               json_encode(object)
+       );
        assert(value->parent == NULL);
        
        value->key = json_strdup(key);
        prepend_node(object, value);
 }
 
+void json_dedup_members(bool b)
+{
+       append_member_node_fn = b ? insert_node : append_node;
+}
+
 void json_remove_from_parent(JsonNode *node)
 {
        JsonNode *parent = node->parent;
@@ -1181,9 +1273,9 @@
 #endif
                                                b += 6;
                                        } else {
-                                               *b++ = 0xEF;
-                                               *b++ = 0xBF;
-                                               *b++ = 0xBD;
+                                               *b++ = (char)0xEF;
+                                               *b++ = (char)0xBF;
+                                               *b++ = (char)0xBD;
                                        }
                                        s++;
                                } else if (c < 0x1F || (c >= 0x80 && 
escape_unicode)) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/json.h new/jo-1.6/json.h
--- old/jo-1.4/json.h   2020-07-18 18:20:38.000000000 +0200
+++ new/jo-1.6/json.h   2022-01-05 16:59:37.000000000 +0100
@@ -101,6 +101,7 @@
 void json_prepend_element(JsonNode *array, JsonNode *element);
 void json_append_member(JsonNode *object, const char *key, JsonNode *value);
 void json_prepend_member(JsonNode *object, const char *key, JsonNode *value);
+void json_dedup_members(bool b);
 
 void json_remove_from_parent(JsonNode *node);
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/meson.build new/jo-1.6/meson.build
--- old/jo-1.4/meson.build      1970-01-01 01:00:00.000000000 +0100
+++ new/jo-1.6/meson.build      2022-01-05 16:59:37.000000000 +0100
@@ -0,0 +1,92 @@
+project('jo', 'c',
+        version: '1.6',
+        license: 'GPL-2.0-or-later',
+        meson_version: '>=0.57.0',
+        default_options: ['warning_level=3', 'optimization=2'])
+
+
+PACKAGE_VERSION = meson.project_version()
+
+cc = meson.get_compiler('c')
+
+headers = [
+    'stddef.h',
+    'stdint.h',
+    'stdlib.h',
+    'string.h',
+    'unistd.h',
+    'stdbool.h'
+]
+
+functions = [
+    'strchr',
+    'strrchr',
+    'strlcpy',
+    'strlcat',
+    'snprintf',
+    'pledge',
+    'err',
+    'errx'
+]
+
+foreach h: headers
+cc.has_header(h, required: true)
+endforeach
+foreach f: functions
+add_project_arguments(
+    '-DHAVE_@0@='.format(f.to_upper()) +
+    cc.has_function(f).to_int().to_string(),
+    language: 'c')
+endforeach
+
+add_project_arguments('-DPACKAGE_VERSION="@0@"'.format(PACKAGE_VERSION),
+                      language: 'c')
+
+
+pandoc = find_program('pandoc', required: false)
+if not pandoc.found()
+warning('pandoc not found, man pages rebuild will not be possible')
+jo1 = 'jo.1'
+else
+pandoc_commands = [pandoc, '-s', '-w', 'man', '-f', 'markdown', '-o']
+jo1 = custom_target('jo.1',
+                    output: 'jo.1',
+                    input: 'jo.pandoc',
+                    build_always_stale: true,
+                    command: [pandoc_commands, '@OUTPUT@', 
'@INPUT@']).full_path()
+run_command(pandoc_commands,
+            join_paths(meson.current_build_dir(), 'jo.1'),
+            join_paths(meson.current_source_dir(), 'jo.pandoc'))
+custom_target('jo.md',
+              output: 'jo.md',
+              input: 'jo.pandoc',
+              build_always_stale: true,
+              command: [pandoc, '-s', '-w', 'markdown+simple_tables', '-f', 
'markdown', '-o', '@OUTPUT@', '@INPUT@'])
+endif
+
+install_man(jo1)
+
+bashcomp = dependency('bash-completion', required: false)
+if bashcomp.found()
+bashcompdir = bashcomp.get_variable(pkgconfig: 'completionsdir')
+else
+bashcompdir = join_paths(get_option('sysconfdir'), 'bash_completion.d')
+endif
+
+install_data('jo.bash', install_dir: bashcompdir)
+
+m_dep = cc.find_library('m', required : false)
+
+executable('jo',
+           'jo.c',
+           'base64.c',
+           'base64.h',
+           'json.c',
+           dependencies: m_dep,
+           install: true)
+
+summary({'Prefix': get_option('prefix'),
+         'C compiler': cc.get_id(),
+         'Pandoc': pandoc,
+         'Bash completion': join_paths(bashcompdir, 'jo.bash'),
+         })
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/rpm-build/jo.spec new/jo-1.6/rpm-build/jo.spec
--- old/jo-1.4/rpm-build/jo.spec        2020-07-18 18:20:38.000000000 +0200
+++ new/jo-1.6/rpm-build/jo.spec        2022-01-05 16:59:37.000000000 +0100
@@ -1,5 +1,5 @@
 Name:           jo
-Version:        1.4
+Version:        1.6
 Release:        2%{?dist}
 Summary:        jo is a small utility to create JSON objects
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/snapcraft.yaml new/jo-1.6/snapcraft.yaml
--- old/jo-1.4/snapcraft.yaml   2020-07-18 18:20:38.000000000 +0200
+++ new/jo-1.6/snapcraft.yaml   2022-01-05 16:59:37.000000000 +0100
@@ -1,16 +1,16 @@
 name: jo
-version: "1.4"
+version: "1.6"
 summary: jo
 description: |
   This is jo, a small utility to create JSON objects or arrays.
 
 confinement: strict
 grade: stable
-base: core
+base: core20
 
 apps:
   jo:
-    command: jo
+    command: usr/local/bin/jo
     plugs: [home, removable-media]
 
 parts:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/tests/jo-creator.txt 
new/jo-1.6/tests/jo-creator.txt
--- old/jo-1.4/tests/jo-creator.txt     1970-01-01 01:00:00.000000000 +0100
+++ new/jo-1.6/tests/jo-creator.txt     2022-01-05 16:59:37.000000000 +0100
@@ -0,0 +1 @@
+Jan-Piet Mens <jpm...@gmail.com>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/tests/jo.08.sh new/jo-1.6/tests/jo.08.sh
--- old/jo-1.4/tests/jo.08.sh   2020-07-18 18:20:38.000000000 +0200
+++ new/jo-1.6/tests/jo.08.sh   2022-01-05 16:59:37.000000000 +0100
@@ -1,2 +1,2 @@
 # data from file: text
-${JO:-jo} program="jo" authors=@${srcdir:=.}/AUTHORS
+${JO:-jo} program="jo" authors=@${srcdir:=.}/tests/jo-creator.txt
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/tests/jo.09.sh new/jo-1.6/tests/jo.09.sh
--- old/jo-1.4/tests/jo.09.sh   2020-07-18 18:20:38.000000000 +0200
+++ new/jo-1.6/tests/jo.09.sh   2022-01-05 16:59:37.000000000 +0100
@@ -1,2 +1,2 @@
 # data from file: base64-encoded
-${JO:-jo} program="jo" authors=%${srcdir:=.}/AUTHORS
+${JO:-jo} program="jo" authors=%${srcdir:=.}/tests/jo-creator.txt
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/tests/jo.17.sh new/jo-1.6/tests/jo.17.sh
--- old/jo-1.4/tests/jo.17.sh   2020-07-18 18:20:38.000000000 +0200
+++ new/jo-1.6/tests/jo.17.sh   2022-01-05 16:59:37.000000000 +0100
@@ -26,5 +26,5 @@
 done
 
 # @/% file inclusions
-${JO:-jo} -- -s s=@${srcdir:=.}/AUTHORS -n n=@${srcdir:=.}/AUTHORS -b 
b=@${srcdir:=.}/AUTHORS a=@${srcdir:=.}/AUTHORS
-${JO:-jo} -- -s s=%${srcdir:=.}/AUTHORS -n n=%${srcdir:=.}/AUTHORS -b 
b=%${srcdir:=.}/AUTHORS a=%${srcdir:=.}/AUTHORS
+${JO:-jo} -- -s s=@${srcdir:=.}/tests/jo-creator.txt -n 
n=@${srcdir:=.}/tests/jo-creator.txt -b b=@${srcdir:=.}/tests/jo-creator.txt 
a=@${srcdir:=.}/tests/jo-creator.txt
+${JO:-jo} -- -s s=%${srcdir:=.}/tests/jo-creator.txt -n 
n=%${srcdir:=.}/tests/jo-creator.txt -b b=%${srcdir:=.}/tests/jo-creator.txt 
a=%${srcdir:=.}/tests/jo-creator.txt
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/tests/jo.21.exp new/jo-1.6/tests/jo.21.exp
--- old/jo-1.4/tests/jo.21.exp  2020-07-18 18:20:38.000000000 +0200
+++ new/jo-1.6/tests/jo.21.exp  2022-01-05 16:59:37.000000000 +0100
@@ -1 +1,2 @@
 {"nested":{"a":1,"b":"val"}}
+{"top":{"obj1":{"c":3,"d":"key"},"obj2":{"a":1,"b":"val"}}}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/tests/jo.21.sh new/jo-1.6/tests/jo.21.sh
--- old/jo-1.4/tests/jo.21.sh   2020-07-18 18:20:38.000000000 +0200
+++ new/jo-1.6/tests/jo.21.sh   2022-01-05 16:59:37.000000000 +0100
@@ -4,4 +4,7 @@
 
 ${JO:-jo} nested=:$$.1
 
+# nested json within object path
+${JO:-jo} -d . top.obj1.c=3 top.obj1.d="key" top.obj2=:$$.1
+
 rm -f $$.1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/tests/jo.23.exp new/jo-1.6/tests/jo.23.exp
--- old/jo-1.4/tests/jo.23.exp  1970-01-01 01:00:00.000000000 +0100
+++ new/jo-1.6/tests/jo.23.exp  2022-01-05 16:59:37.000000000 +0100
@@ -0,0 +1,6 @@
+{"foo":null}
+{}
+{"foo":1,"bar":null,"baz":3}
+{"foo":1,"baz":3}
+{"list":[1,null]}
+{"list":[1]}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/tests/jo.23.sh new/jo-1.6/tests/jo.23.sh
--- old/jo-1.4/tests/jo.23.sh   1970-01-01 01:00:00.000000000 +0100
+++ new/jo-1.6/tests/jo.23.sh   2022-01-05 16:59:37.000000000 +0100
@@ -0,0 +1,10 @@
+# disable creation of null-valued keys
+
+${JO:-jo} foo=
+${JO:-jo} -n foo=
+${JO:-jo} foo=1 bar= baz=3
+${JO:-jo} -n foo=1 bar= baz=3
+nothing=
+${JO:-jo} list[]=1 list[]=$nothing
+${JO:-jo} -n list[]=1 list[]=$nothing
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/tests/jo.24.exp new/jo-1.6/tests/jo.24.exp
--- old/jo-1.4/tests/jo.24.exp  1970-01-01 01:00:00.000000000 +0100
+++ new/jo-1.6/tests/jo.24.exp  2022-01-05 16:59:37.000000000 +0100
@@ -0,0 +1,8 @@
+[1,2,3,4,6,8]
+{"a":1,"b":2,"c":42,"d":3}
+{"a":1,"b":2,"c":42,"d":3,"stage":{"1":"a","2":"b"}}
+{"a":1,"b":2,"c":42,"d":3,"stage":{"1":"a","2":"b"}}
+{"a":1,"b":2,"c":42,"d":3,"stage":{"1":"a","2":"b"}}
+{"a":1,"b":2,"stage":{"1":"a","2":"b"}}
+{"people":"need people"}
+{"people":"need people"}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/tests/jo.24.sh new/jo-1.6/tests/jo.24.sh
--- old/jo-1.4/tests/jo.24.sh   1970-01-01 01:00:00.000000000 +0100
+++ new/jo-1.6/tests/jo.24.sh   2022-01-05 16:59:37.000000000 +0100
@@ -0,0 +1,28 @@
+# jo as filter
+
+# filter array
+echo "[1,2,3,4]" | ${JO:-jo} -f - 6 8
+
+# filter object
+${JO:-jo} a=1 b=2 | ${JO:-jo} -f - c=42 d=3
+
+# multi-stage pipeline
+${JO:-jo} a=1 b=2 | ${JO:-jo} -f - c=42 d=3 | ${JO:-jo} -f - -d . stage.1=a 
stage.2=b
+
+# filter from file
+tmp=/tmp/jo.filter.$$
+trap "rm -f $tmp; exit" 0 1 2 15
+
+${JO:-jo} a=1 b=2 > $tmp
+${JO:-jo} -f $tmp c=42 d=3 | ${JO:-jo} -f - -d . stage.1=a stage.2=b
+
+# take initial object from file, and mods from stdin
+echo "c=42
+d=3" | ${JO:-jo} -f $tmp | ${JO:-jo} -f - -d . stage.1=a stage.2=b
+
+# this command should NOT output keys "c" and "d"
+${JO:-jo} a=1 b=2 | ${JO:-jo} -f - c=42 d=3 | ${JO:-jo} -f $tmp -d . stage.1=a 
stage.2=b
+
+# filter non-collections (input basically ignored)
+echo hi | tee $tmp | ${JO:-jo} -f - people="need people"
+${JO:-jo} -f $tmp people="need people"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/tests/jo.25.exp new/jo-1.6/tests/jo.25.exp
--- old/jo-1.4/tests/jo.25.exp  1970-01-01 01:00:00.000000000 +0100
+++ new/jo-1.6/tests/jo.25.exp  2022-01-05 16:59:37.000000000 +0100
@@ -0,0 +1,6 @@
+{"a":1,"b":2,"a":3}
+{"a":3,"b":2}
+{"stage":{"1":"a","2":"b","3":"c","2":"x","3":"y","4":"d","1":"h"},"down":"up"}
+{"stage":{"1":"h","2":"x","3":"y","4":"d"},"down":"up"}
+{"name":"aaa"}
+{"name":"cba","another_name":"abc"}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/tests/jo.25.sh new/jo-1.6/tests/jo.25.sh
--- old/jo-1.4/tests/jo.25.sh   1970-01-01 01:00:00.000000000 +0100
+++ new/jo-1.6/tests/jo.25.sh   2022-01-05 16:59:37.000000000 +0100
@@ -0,0 +1,14 @@
+# overwrite values of existing object keys
+
+${JO:-jo} a=1 b=2 a=3
+${JO:-jo} -D a=1 b=2 a=3
+
+tmp=`${JO:-jo} 1=a 2=b 3=c`
+${JO:-jo} -d . stage="$tmp" down=up stage.2=x stage\[3\]=y stage.4=d 
stage\[1\]=h
+${JO:-jo} -D -d . stage="$tmp" down=up stage.2=x stage\[3\]=y stage.4=d 
stage\[1\]=h
+
+# dedup filter input too
+tmpf=$$.json
+trap 'rm -f "$tmpf"' 0 1 2 15
+${JO:-jo} name=aaa name=aaa | tee $tmpf | ${JO:-jo} -D -f - name=aaa
+${JO:-jo} -D -f $tmpf another_name=abc name=cba
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/tests/jo.26.exp new/jo-1.6/tests/jo.26.exp
--- old/jo-1.4/tests/jo.26.exp  1970-01-01 01:00:00.000000000 +0100
+++ new/jo-1.6/tests/jo.26.exp  2022-01-05 16:59:37.000000000 +0100
@@ -0,0 +1 @@
+{"file":"stdin","jo":true}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/tests/jo.26.sh new/jo-1.6/tests/jo.26.sh
--- old/jo-1.4/tests/jo.26.sh   1970-01-01 01:00:00.000000000 +0100
+++ new/jo-1.6/tests/jo.26.sh   2022-01-05 16:59:37.000000000 +0100
@@ -0,0 +1,2 @@
+# read from stdin
+echo '{"file":"stdin", "jo": true}' | ${JO:-jo} -f -
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/tests/jo.27.exp new/jo-1.6/tests/jo.27.exp
--- old/jo-1.4/tests/jo.27.exp  1970-01-01 01:00:00.000000000 +0100
+++ new/jo-1.6/tests/jo.27.exp  2022-01-05 16:59:37.000000000 +0100
@@ -0,0 +1,6 @@
+{"b":[1,2]}
+jo: JSON_ERR: Cannot add {"a":3} to non-object [1]
+Test 2 should fail
+{"d":{"m":10,"n":20}}
+jo: JSON_ERR: Cannot append 20 to non-array {"m":10}
+Test 4 should fail
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/jo-1.4/tests/jo.27.sh new/jo-1.6/tests/jo.27.sh
--- old/jo-1.4/tests/jo.27.sh   1970-01-01 01:00:00.000000000 +0100
+++ new/jo-1.6/tests/jo.27.sh   2022-01-05 16:59:37.000000000 +0100
@@ -0,0 +1,6 @@
+# user-friendly errors
+${JO:-jo} b[]=1 b[]=2
+${JO:-jo} b[]=1 b[a]=3 2>&1 || echo "Test 2 should fail"
+
+${JO:-jo} d[m]=10 d[n]=20
+${JO:-jo} d[m]=10 d[]=20 2>&1 || echo "Test 4 should fail"

Reply via email to