Hello community, here is the log from the commit of package jo for openSUSE:Factory checked in at 2020-07-24 09:59:23 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/jo (Old) and /work/SRC/openSUSE:Factory/.jo.new.3592 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "jo" Fri Jul 24 09:59:23 2020 rev:4 rq:822179 version:1.4 Changes: -------- --- /work/SRC/openSUSE:Factory/jo/jo.changes 2020-03-11 18:51:06.411561145 +0100 +++ /work/SRC/openSUSE:Factory/.jo.new.3592/jo.changes 2020-07-24 10:01:06.141707064 +0200 @@ -1,0 +2,9 @@ +Mon Jul 20 20:47:55 UTC 2020 - Martin Hauke <mar...@gmx.de> + +- Update to versrion 1.4 + * FIX: Coercion flag logic now permits getopt(3) double-dash + * FIX: Documentation clarifies special characters + * FIX: Jo builds on snap builds + * FIX: Jo builds on systems with slightly older pkg-config + +------------------------------------------------------------------- Old: ---- jo-1.3.tar.gz New: ---- jo-1.4.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ jo.spec ++++++ --- /var/tmp/diff_new_pack.3tsD8k/_old 2020-07-24 10:01:10.029710684 +0200 +++ /var/tmp/diff_new_pack.3tsD8k/_new 2020-07-24 10:01:10.033710688 +0200 @@ -17,7 +17,7 @@ Name: jo -Version: 1.3 +Version: 1.4 Release: 0 Summary: JSON output from a shell License: GPL-2.0-or-later AND MIT ++++++ jo-1.3.tar.gz -> jo-1.4.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jo-1.3/ChangeLog new/jo-1.4/ChangeLog --- old/jo-1.3/ChangeLog 2019-11-04 09:40:39.000000000 +0100 +++ new/jo-1.4/ChangeLog 2020-07-18 18:20:38.000000000 +0200 @@ -1,3 +1,10 @@ +2020-07-18 1.4 + +- FIX: Coercion flag logic now permits getopt(3) double-dash +- FIX: Documentation clarifies special characters +- FIX: Jo builds on snap builds (#110) +- FIX: Jo builds on systems with slightly older pkg-config (#107) + 2019-11-04 1.3 - FIX: Escaped @ ("\@") is treated as "@" (#42, #103) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jo-1.3/Dockerfile new/jo-1.4/Dockerfile --- old/jo-1.3/Dockerfile 2019-11-04 09:40:39.000000000 +0100 +++ new/jo-1.4/Dockerfile 2020-07-18 18:20:38.000000000 +0200 @@ -1,5 +1,5 @@ FROM alpine AS builder -RUN apk -U add automake autoconf build-base make +RUN apk -U add automake autoconf build-base make pkgconf COPY . /src WORKDIR /src RUN autoreconf -i && ./configure && make check && make install diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jo-1.3/README.md new/jo-1.4/README.md --- old/jo-1.3/README.md 2019-11-04 09:40:39.000000000 +0100 +++ new/jo-1.4/README.md 2020-07-18 18:20:38.000000000 +0200 @@ -27,8 +27,9 @@ To build from [a release](https://github.com/jpmens/jo/releases) you will need a C compiler to install from a source tarball which you download from the [Releases page](https://github.com/jpmens/jo/releases). ```bash -tar xvzf jo-1.0.tar.gz -cd jo-1.0 +tar xvzf jo-1.3.tar.gz +cd jo-1.3 +autoreconf -i ./configure make check make install @@ -58,14 +59,16 @@ ## Ubuntu -To install on Ubuntu, use [this PPA](https://launchpad.net/~duggan/+archive/ubuntu/jo): - ``` -apt-add-repository ppa:duggan/jo --yes -apt-get update -q apt-get install jo ``` +## Gentoo + +``` +emerge jo +``` + ## Snap Thanks to [Roger Light](https://twitter.com/ralight/status/1166023769623867398), _jo_ is available as a [snap package](https://snapcraft.io/jo). Use `snap install jo` from a Linux distro that supports snaps. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jo-1.3/configure.ac new/jo-1.4/configure.ac --- old/jo-1.3/configure.ac 2019-11-04 09:40:39.000000000 +0100 +++ new/jo-1.4/configure.ac 2020-07-18 18:20:38.000000000 +0200 @@ -1,5 +1,5 @@ AC_PREREQ([2.63]) -AC_INIT([jo], [1.3], [j...@mens.de]) +AC_INIT([jo], [1.4], [j...@mens.de]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_SRCDIR([jo.c]) @@ -16,6 +16,19 @@ AC_FUNC_STRTOD AC_CHECK_FUNCS([strchr strrchr strlcpy strlcat snprintf pledge err errx]) +# backport PKG_CHECK_VAR from pkgconfig 0.29 +m4_ifndef([PKG_CHECK_VAR], [AC_DEFUN([PKG_CHECK_VAR], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl + +_PKG_CONFIG([$1], [variable="][$3]["], [$2]) +AS_VAR_COPY([$1], [pkg_cv_][$1]) + +AS_VAR_IF([$1], [""], [$5], [$4])dnl +])dnl PKG_CHECK_VAR +]) + + AM_INIT_AUTOMAKE([foreign -Wall]) AM_SILENT_RULES([yes]) AC_REQUIRE_AUX_FILE([tap-driver.sh]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jo-1.3/jo.1 new/jo-1.4/jo.1 --- old/jo-1.3/jo.1 2019-11-04 09:40:39.000000000 +0100 +++ new/jo-1.4/jo.1 2020-07-18 18:20:38.000000000 +0200 @@ -1,5 +1,5 @@ .\"t -.\" Automatically generated by Pandoc 1.16.0.2 +.\" Automatically generated by Pandoc 2.5 .\" .TH "JO" "1" "" "User Manuals" "" .hy @@ -8,46 +8,81 @@ jo \- JSON output from a shell .SH SYNOPSIS .PP -jo [\-p] [\-a] [\-B] [\-e] [\-v] [\-V] [\-d keydelim] [\-\-] [ -[\-s|\-n|\-b] word ...] +jo [\-p] [\-a] [\-B] [\-e] [\-v] [\-V] [\-d keydelim] [\[en]] [ +[\-s|\-n|\-b] word \&...] .SH DESCRIPTION .PP -\f[I]jo\f[] creates a JSON string on \f[I]stdout\f[] from _word_s given -it as arguments or read from \f[I]stdin\f[]. -Without option \f[C]\-a\f[] it generates an object whereby each -\f[I]word\f[] is a \f[C]key=value\f[] (or \f[C]key\@value\f[]) pair with -\f[I]key\f[] being the JSON object element and \f[I]value\f[] its value. -\f[I]jo\f[] attempts to guess the type of \f[I]value\f[] in order to -create number (using \f[I]strtod(3)\f[]), string, or null values in +\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]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. +\f[I]jo\f[R] attempts to guess the type of \f[I]value\f[R] in order to +create number (using \f[I]strtod(3)\f[R]), string, or null values in JSON. .PP -\f[I]jo\f[] normally treats \f[I]key\f[] as a literal string value. -If the \f[C]\-d\f[] option is specified, \f[I]key\f[] will be -interpreted as an \f[I]object path\f[], whose individual components are -separated by the first character of \f[I]keydelim\f[]. -.PP -\f[I]jo\f[] treats \f[C]key\@value\f[] specifically as boolean JSON -elements: if the value begins with \f[C]T\f[], \f[C]t\f[], or the -numeric value is greater than zero, the result is \f[C]true\f[], else -\f[C]false\f[]. -A missing or empty value behind the colon results in a \f[C]null\f[] +\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 +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 +\f[I]jo\f[R] normally treats \f[I]value\f[R] as a literal string value, +unless it begins with one of the following characters: +.PP +.TS +tab(@); +l l. +T{ +value +T}@T{ +action +T} +_ +T{ +\[at]file +T}@T{ +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 +T} +T{ +:file +T}@T{ +interpret the contents of \f[I]file\f[R] as JSON, and substitute the +result +T} +.TE +.PP +Escape the special character with a backslash to prevent this +interpretation. +.PP +\f[I]jo\f[R] treats \f[C]key\[at]value\f[R] specifically as boolean JSON +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[] creates an array instead of an object when \f[C]\-a\f[] 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[] operator is used in a \f[I]word\f[], the name to -the right of \f[C]:=\f[] is a file containing JSON which is parsed and +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[] to read from \f[I]jo\f[]\[aq]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[]\[aq]s type guesses can be overridden on a per\-word basis by -prefixing \f[I]word\f[] with \f[C]\-s\f[] for \f[I]string\f[], -\f[C]\-n\f[] for \f[I]number\f[], or \f[C]\-b\f[] for \f[I]boolean\f[]. -The list of _word_s \f[I]must\f[] be prefixed with \f[C]\-\-\f[], to -indicate to \f[I]jo\f[] that there are no more global options. +\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. .PP Type coercion works as follows: .PP @@ -69,90 +104,90 @@ T{ a= T}@T{ -"a":"" +\[lq]a\[rq]:\[dq]\[dq] T}@T{ -"a":0 +\[lq]a\[rq]:0 T}@T{ -"a":false +\[lq]a\[rq]:false T}@T{ -"a":null +\[lq]a\[rq]:null T} T{ a=string T}@T{ -"a":"string" +\[lq]a\[rq]:\[lq]string\[rq] T}@T{ -"a":6 +\[lq]a\[rq]:6 T}@T{ -"a":true +\[lq]a\[rq]:true T}@T{ -"a":"string" +\[lq]a\[rq]:\[lq]string\[rq] T} T{ -a="quoted" +a=\[dq]quoted\[dq] T}@T{ -"a":""quoted"" +\[lq]a\[rq]:\[lq]\[dq]quoted\[dq]\[rq] T}@T{ -"a":8 +\[lq]a\[rq]:8 T}@T{ -"a":true +\[lq]a\[rq]:true T}@T{ -"a":""quoted"" +\[lq]a\[rq]:\[lq]\[dq]quoted\[dq]\[rq] T} T{ a=12345 T}@T{ -"a":"12345" +\[lq]a\[rq]:\[lq]12345\[rq] T}@T{ -"a":12345 +\[lq]a\[rq]:12345 T}@T{ -"a":true +\[lq]a\[rq]:true T}@T{ -"a":12345 +\[lq]a\[rq]:12345 T} T{ a=true T}@T{ -"a":"true" +\[lq]a\[rq]:\[lq]true\[rq] T}@T{ -"a":1 +\[lq]a\[rq]:1 T}@T{ -"a":true +\[lq]a\[rq]:true T}@T{ -"a":true +\[lq]a\[rq]:true T} T{ a=false T}@T{ -"a":"false" +\[lq]a\[rq]:\[lq]false\[rq] T}@T{ -"a":0 +\[lq]a\[rq]:0 T}@T{ -"a":false +\[lq]a\[rq]:false T}@T{ -"a":false +\[lq]a\[rq]:false T} T{ a=null T}@T{ -"a":"" +\[lq]a\[rq]:\[dq]\[dq] T}@T{ -"a":0 +\[lq]a\[rq]:0 T}@T{ -"a":false +\[lq]a\[rq]:false T}@T{ -"a":null +\[lq]a\[rq]:null T} .TE .PP -Coercing a non\-number string to number outputs the \f[I]length\f[] 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[] if the -string is empty, \f[C]true\f[] otherwise. +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[] words, and individual -words in a \f[C]\-a\f[] array. +Type coercion only applies to \f[C]key=value\f[R] words, and individual +words in a \f[C]\-a\f[R] array. Coercing other words has no effect. .SH EXAMPLES .PP @@ -161,180 +196,196 @@ .IP .nf \f[C] -$\ jo\ tst=1457081292\ lat=12.3456\ cc=FR\ badfloat=3.14159.26\ name="JP\ Mens"\ nada=\ coffee\@T -{"tst":1457081292,"lat":12.3456,"cc":"FR","badfloat":"3.14159.26","name":"JP\ Mens","nada":null,"coffee":true} -\f[] +$ jo tst=1457081292 lat=12.3456 cc=FR badfloat=3.14159.26 name=\[dq]JP Mens\[dq] nada= coffee\[at]T +{\[dq]tst\[dq]:1457081292,\[dq]lat\[dq]:12.3456,\[dq]cc\[dq]:\[dq]FR\[dq],\[dq]badfloat\[dq]:\[dq]3.14159.26\[dq],\[dq]name\[dq]:\[dq]JP Mens\[dq],\[dq]nada\[dq]:null,\[dq]coffee\[dq]:true} +\f[R] .fi .PP Pretty\-print an array with a list of files in the current directory: .IP .nf \f[C] -$\ jo\ \-p\ \-a\ * +$ jo \-p \-a * [ -\ "Makefile", -\ "README.md", -\ "jo.1", -\ "jo.c", -\ "jo.pandoc", -\ "json.c", -\ "json.h" + \[dq]Makefile\[dq], + \[dq]README.md\[dq], + \[dq]jo.1\[dq], + \[dq]jo.c\[dq], + \[dq]jo.pandoc\[dq], + \[dq]json.c\[dq], + \[dq]json.h\[dq] ] -\f[] +\f[R] .fi .PP Create objects within objects; this works because if the first character of value is an open brace or a bracket we attempt to decode the remainder as JSON. -Beware spaces in strings ... +Beware spaces in strings \&... .IP .nf \f[C] -$\ jo\ \-p\ name=JP\ object=$(jo\ fruit=Orange\ hungry\@0\ point=$(jo\ x=10\ y=20\ list=$(jo\ \-a\ 1\ 2\ 3\ 4\ 5))\ number=17)\ sunday\@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 { -\ "name":\ "JP", -\ "object":\ { -\ \ "fruit":\ "Orange", -\ \ "hungry":\ false, -\ \ "point":\ { -\ \ \ "x":\ 10, -\ \ \ "y":\ 20, -\ \ \ "list":\ [ -\ \ \ \ 1, -\ \ \ \ 2, -\ \ \ \ 3, -\ \ \ \ 4, -\ \ \ \ 5 -\ \ \ ] -\ \ }, -\ \ "number":\ 17 -\ }, -\ "sunday":\ false + \[dq]name\[dq]: \[dq]JP\[dq], + \[dq]object\[dq]: { + \[dq]fruit\[dq]: \[dq]Orange\[dq], + \[dq]hungry\[dq]: false, + \[dq]point\[dq]: { + \[dq]x\[dq]: 10, + \[dq]y\[dq]: 20, + \[dq]list\[dq]: [ + 1, + 2, + 3, + 4, + 5 + ] + }, + \[dq]number\[dq]: 17 + }, + \[dq]sunday\[dq]: false } -\f[] +\f[R] .fi .PP Booleans as strings or as boolean (pay particular attention to -\f[I]switch\f[]; the \f[C]\-B\f[] option disables the default detection -of the "\f[C]true\f[]", "\f[C]false\f[]", and "\f[C]null\f[]" 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\@0 -{"switch":true,"morning":false} +$ jo switch=true morning\[at]0 +{\[dq]switch\[dq]:true,\[dq]morning\[dq]:false} -$\ jo\ \-B\ switch=true\ morning\@0 -{"switch":"true","morning":false} -\f[] +$ jo \-B switch=true morning\[at]0 +{\[dq]switch\[dq]:\[dq]true\[dq],\[dq]morning\[dq]:false} +\f[R] .fi .PP Elements (objects and arrays) can be nested. -The following example nests an array called \f[I]point\f[] and an object -named \f[I]geo\f[]: +The following example nests an array called \f[I]point\f[R] and an +object named \f[I]geo\f[R]: .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 { -\ \ \ "name":\ "Jane", -\ \ \ "point":\ [ -\ \ \ \ \ \ 1, -\ \ \ \ \ \ 2 -\ \ \ ], -\ \ \ "geo":\ { -\ \ \ \ \ \ "lat":\ 10, -\ \ \ \ \ \ "lon":\ 20 -\ \ \ } + \[dq]name\[dq]: \[dq]Jane\[dq], + \[dq]point\[dq]: [ + 1, + 2 + ], + \[dq]geo\[dq]: { + \[dq]lat\[dq]: 10, + \[dq]lon\[dq]: 20 + } } -\f[] +\f[R] .fi .PP The same example, using object paths: .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 { -\ \ \ "name":\ "Jane", -\ \ \ "point":\ [ -\ \ \ \ \ \ 1, -\ \ \ \ \ \ 2 -\ \ \ ], -\ \ \ "geo":\ { -\ \ \ \ \ \ "lat":\ 10, -\ \ \ \ \ \ "lon":\ 20 -\ \ \ } + \[dq]name\[dq]: \[dq]Jane\[dq], + \[dq]point\[dq]: [ + 1, + 2 + ], + \[dq]geo\[dq]: { + \[dq]lat\[dq]: 10, + \[dq]lon\[dq]: 20 + } } -\f[] +\f[R] .fi .PP -Without \f[C]\-d\f[], 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 { -\ \ \ "name":\ "Jane", -\ \ \ "point":\ [ -\ \ \ \ \ \ 1, -\ \ \ \ \ \ 2 -\ \ \ ], -\ \ \ "geo.lat":\ 10, -\ \ \ "geo.lon":\ 20 + \[dq]name\[dq]: \[dq]Jane\[dq], + \[dq]point\[dq]: [ + 1, + 2 + ], + \[dq]geo.lat\[dq]: 10, + \[dq]geo.lon\[dq]: 20 } -\f[] +\f[R] .fi .PP Create empty objects or arrays, intentionally or potentially: .IP .nf \f[C] -$\ jo\ <\ /dev/null +$ jo < /dev/null {} -$\ MY_ARRAY=(a=1\ b=2) -$\ jo\ \-a\ "${MY_ARRAY[\@]}"\ <\ /dev/null -["a=1","b=2"] -\f[] +$ MY_ARRAY=(a=1 b=2) +$ jo \-a \[dq]${MY_ARRAY[\[at]]}\[dq] < /dev/null +[\[dq]a=1\[dq],\[dq]b=2\[dq]] +\f[R] .fi .PP Type coercion: .IP .nf \f[C] -$\ jo\ \-p\ \-\-\ \-s\ a=true\ b=true\ \-s\ c=123\ d=123\ \-b\ e="1"\ \-b\ f="true"\ \-n\ g="This\ is\ a\ test"\ \-b\ h="This\ is\ a\ test" +$ 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] { -\ \ \ "a":\ "true", -\ \ \ "b":\ true, -\ \ \ "c":\ "123", -\ \ \ "d":\ 123, -\ \ \ "e":\ true, -\ \ \ "f":\ true, -\ \ \ "g":\ 14, -\ \ \ "h":\ true + \[dq]a\[dq]: \[dq]true\[dq], + \[dq]b\[dq]: true, + \[dq]c\[dq]: \[dq]123\[dq], + \[dq]d\[dq]: 123, + \[dq]e\[dq]: true, + \[dq]f\[dq]: true, + \[dq]g\[dq]: 14, + \[dq]h\[dq]: true } -$\ jo\ \-a\ \-\-\ \-s\ 123\ \-n\ "This\ is\ a\ test"\ \-b\ C_Rocks\ 456 -["123",14,true,456] -\f[] +$ 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: +.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]} + +$ jo filename=AUTHORS content=%AUTHORS +{\[dq]filename\[dq]:\[dq]AUTHORS\[dq],\[dq]content\[dq]:\[dq]SmFuLVBpZXQgTWVucyA8anBtZW5zQGdtYWlsLmNvbT4K\[dq]} + +$ jo nested=:nested.json +{\[dq]nested\[dq]:{\[dq]field1\[dq]:123,\[dq]field2\[dq]:\[dq]abc\[dq]}} +\f[R] .fi .PP -Read element values from files: a value which starts with \f[C]\@\f[] is -read in plain whereas if it begins with a \f[C]%\f[] it will be -base64\-encoded and if it starts with \f[C]:\f[] the contents are -interpreted as JSON: +These characters can be escaped to avoid interpretation: .IP .nf \f[C] -$\ jo\ program=jo\ authors=\@AUTHORS -{"program":"jo","authors":"Jan\-Piet\ Mens\ <jpmens\@gmail.com>"} +$ jo name=\[dq]JP Mens\[dq] twitter=\[aq]\[rs]\[at]jpmens\[aq] +{\[dq]name\[dq]:\[dq]JP Mens\[dq],\[dq]twitter\[dq]:\[dq]\[at]jpmens\[dq]} -$\ jo\ filename=AUTHORS\ content=%AUTHORS -{"filename":"AUTHORS","content":"SmFuLVBpZXQgTWVucyA8anBtZW5zQGdtYWlsLmNvbT4K"} +$ jo char=\[dq] \[dq] URIescape=\[rs]\[rs]%20 +{\[dq]char\[dq]:\[dq] \[dq],\[dq]URIescape\[dq]:\[dq]%20\[dq]} -$\ jo\ nested=:nested.json -{"nested":{"field1":123,"field2":"abc"}} -\f[] +$ jo action=\[dq]split window\[dq] vimcmd=\[dq]\[rs]:split\[dq] +{\[dq]action\[dq]:\[dq]split window\[dq],\[dq]vimcmd\[dq]:\[dq]:split\[dq]} +\f[R] .fi .PP Read element values from a file in order to overcome ARG_MAX limits @@ -342,76 +393,65 @@ .IP .nf \f[C] -$\ ls\ |\ jo\ \-a\ >\ child.json -$\ jo\ files:=child.json -{"files":["AUTHORS","COPYING","ChangeLog"\ .... +$ 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 -[["base64.c","jo.c","json.c"],["base64.h","json.h"]] -\f[] +$ 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 .SH OPTIONS .PP -\f[I]jo\f[] understands the following global options. +\f[I]jo\f[R] understands the following global options. .TP .B \-a -Interpret the list of \f[I]words\f[] as array values and produce an +Interpret the list of \f[I]words\f[R] as array values and produce an array instead of an object. -.RS -.RE .TP .B \-B -By default \f[I]jo\f[] interprets the strings "\f[C]true\f[]" and -"\f[C]false\f[]" as boolean elements \f[C]true\f[] and \f[C]false\f[] -respectively, and "\f[C]null\f[]" as \f[C]null\f[]. +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. -.RS -.RE .TP .B \-e -Ignore empty stdin (i.e. -don\[aq]t produce a diagnostic error when \f[I]stdin\f[] is empty) -.RS -.RE +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 output it prints by default. -.RS -.RE .TP .B \-v Show version and exit. -.RS -.RE .TP .B \-V Show version as a JSON object and exit. -.RS -.RE .SH BUGS .PP Probably. .PP -If a value given to \f[I]jo\f[] expands to empty in the shell, then -\f[I]jo\f[] produces a \f[C]null\f[] in object mode, and might appear to -hang in array mode; it is not hanging, rather it\[aq]s reading -\f[I]stdin\f[]. +If a value given to \f[I]jo\f[R] expands to empty in the shell, then +\f[I]jo\f[R] produces a \f[C]null\f[R] in object mode, and might appear +to hang in array mode; it is not hanging, rather it\[cq]s reading +\f[I]stdin\f[R]. This is not a bug. .PP Numeric values are converted to numbers which can produce undesired results. -If you quote a numeric value, \f[I]jo\f[] will make it a string. +If you quote a numeric value, \f[I]jo\f[R] will make it a string. Compare the following: .IP .nf \f[C] -$\ jo\ a=1.0 -{"a":1} -$\ jo\ a=\\"1.0\\" -{"a":"1.0"} -\f[] +$ jo a=1.0 +{\[dq]a\[dq]:1} +$ jo a=\[rs]\[dq]1.0\[rs]\[dq] +{\[dq]a\[dq]:\[dq]1.0\[dq]} +\f[R] .fi .PP Omitting a closing bracket on a nested element causes a diagnostic @@ -419,14 +459,14 @@ This was designed thusly. .SH RETURN CODES .PP -\f[I]jo\f[] 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 <http://github.com/jpmens/jo> .SH CREDITS .IP \[bu] 2 -This program uses \f[C]json.[ch]\f[], by Joseph A. +This program uses \f[C]json.[ch]\f[R], by Joseph A. Adams. .SH SEE ALSO .IP \[bu] 2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jo-1.3/jo.c new/jo-1.4/jo.c --- old/jo-1.3/jo.c 2019-11-04 09:40:39.000000000 +0100 +++ new/jo-1.4/jo.c 2020-07-18 18:20:38.000000000 +0200 @@ -50,14 +50,16 @@ # define ftello ftell #endif +#define TAG_TO_FLAGS(tag) ((FLAG_MASK + 1) * (tag)) +#define TAG_FLAG_BOOL (TAG_TO_FLAGS(JSON_BOOL)) +#define TAG_FLAG_STRING (TAG_TO_FLAGS(JSON_STRING)) +#define TAG_FLAG_NUMBER (TAG_TO_FLAGS(JSON_NUMBER)) +#define COERCE_MASK (TAG_FLAG_BOOL | TAG_FLAG_STRING | TAG_FLAG_NUMBER) + JsonTag flags_to_tag(int flags) { return flags / (FLAG_MASK + 1); } -int tag_to_flags(JsonTag tag) { - return (FLAG_MASK + 1) * tag; -} - void json_copy_to_object(JsonNode * obj, JsonNode * object_or_array, int clobber) { JsonNode *node, *node_child, *obj_child; @@ -666,27 +668,32 @@ } } else { while ((kv = *argv++)) { - if (kv[0] == '-') { + if (kv[0] == '-' && !(flags & COERCE_MASK)) { /* Set one-shot coerce flag */ switch (kv[1]) { case 'b': - flags |= tag_to_flags(JSON_BOOL); + flags |= TAG_FLAG_BOOL; break; case 's': - flags |= tag_to_flags(JSON_STRING); + flags |= TAG_FLAG_STRING; break; case 'n': - flags |= tag_to_flags(JSON_NUMBER); + flags |= TAG_FLAG_NUMBER; break; default: - exit(usage(progname)); + /* Treat as normal input */ + p = utf8_from_locale(kv, -1); + append_kv(json, flags, key_delim, p); + utf8_free(p); + /* Reset any one-shot coerce flags */ + flags &= ~(COERCE_MASK); } } else { p = utf8_from_locale(kv, -1); append_kv(json, flags, key_delim, p); utf8_free(p); /* Reset any one-shot coerce flags */ - flags &= FLAG_MASK; + flags &= ~(COERCE_MASK); } } } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jo-1.3/jo.md new/jo-1.4/jo.md --- old/jo-1.3/jo.md 2019-11-04 09:40:39.000000000 +0100 +++ new/jo-1.4/jo.md 2020-07-18 18:20:38.000000000 +0200 @@ -11,7 +11,7 @@ ======== jo \[-p\] \[-a\] \[-B\] \[-e\] \[-v\] \[-V\] \[-d keydelim\] \[--\] \[ -\[-s|-n|-b\] word ...\] +\[-s\|-n\|-b\] word ...\] DESCRIPTION =========== @@ -28,6 +28,18 @@ individual components are separated by the first character of *keydelim*. +*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 + +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 @@ -50,15 +62,15 @@ Type coercion works as follows: - word -s -n -b default - ------------ ---------------- ----------- ----------- ---------------- - a= "a":"" "a":0 "a":false "a":null - a=string "a":"string" "a":6 "a":true "a":"string" - a="quoted" "a":""quoted"" "a":8 "a":true "a":""quoted"" - a=12345 "a":"12345" "a":12345 "a":true "a":12345 - a=true "a":"true" "a":1 "a":true "a":true - a=false "a":"false" "a":0 "a":false "a":false - a=null "a":"" "a":0 "a":false "a":null + word -s -n -b default + -------------- ------------------ ----------- ----------- ------------------ + a= "a":\"\" "a":0 "a":false "a":null + a=string "a":"string" "a":6 "a":true "a":"string" + a=\"quoted\" "a":"\"quoted\"" "a":8 "a":true "a":"\"quoted\"" + a=12345 "a":"12345" "a":12345 "a":true "a":12345 + a=true "a":"true" "a":1 "a":true "a":true + a=false "a":"false" "a":0 "a":false "a":false + a=null "a":\"\" "a":0 "a":false "a":null Coercing a non-number string to number outputs the *length* of the string. @@ -210,6 +222,17 @@ $ jo nested=:nested.json {"nested":{"field1":123,"field2":"abc"}} +These characters can be escaped to avoid interpretation: + + $ jo name="JP Mens" twitter='\@jpmens' + {"name":"JP Mens","twitter":"@jpmens"} + + $ jo char=" " URIescape=\\%20 + {"char":" ","URIescape":"%20"} + + $ jo action="split window" vimcmd="\:split" + {"action":"split window","vimcmd":":split"} + Read element values from a file in order to overcome ARG\_MAX limits during object assignment: @@ -236,7 +259,7 @@ `null`. Disable with this option. -e -: Ignore empty stdin (i.e. don't produce a diagnostic error when +: Ignore empty stdin (i.e. don't produce a diagnostic error when *stdin* is empty) -p diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jo-1.3/jo.pandoc new/jo-1.4/jo.pandoc --- old/jo-1.3/jo.pandoc 2019-11-04 09:40:39.000000000 +0100 +++ new/jo-1.4/jo.pandoc 2020-07-18 18:20:38.000000000 +0200 @@ -18,6 +18,16 @@ *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_. +*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 + +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. @@ -185,6 +195,17 @@ $ jo nested=:nested.json {"nested":{"field1":123,"field2":"abc"}} +These characters can be escaped to avoid interpretation: + + $ jo name="JP Mens" twitter='\@jpmens' + {"name":"JP Mens","twitter":"@jpmens"} + + $ jo char=" " URIescape=\\%20 + {"char":" ","URIescape":"%20"} + + $ jo action="split window" vimcmd="\:split" + {"action":"split window","vimcmd":":split"} + Read element values from a file in order to overcome ARG_MAX limits during object assignment: $ ls | jo -a > child.json diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jo-1.3/rpm-build/jo.spec new/jo-1.4/rpm-build/jo.spec --- old/jo-1.3/rpm-build/jo.spec 2019-11-04 09:40:39.000000000 +0100 +++ new/jo-1.4/rpm-build/jo.spec 2020-07-18 18:20:38.000000000 +0200 @@ -1,11 +1,11 @@ Name: jo -Version: 1.1 -Release: 1%{?dist} +Version: 1.4 +Release: 2%{?dist} Summary: jo is a small utility to create JSON objects License: GPL2 URL: https://github.com/jpmens/jo -Source0: https://github.com/jpmens/jo/releases/download/v%{version}/jo-%{version}.tar.gz +Source0: https://github.com/jpmens/jo/releases/download/%{version}/jo-%{version}.tar.gz BuildRequires: autoconf BuildRequires: pandoc @@ -31,9 +31,22 @@ %doc %{_bindir}/* %{_mandir}/man1/* +%if 0%{?suse_version} +%{_datadir}/bash-completion/completions +%else +%{_sysconfdir}/bash_completion.d/%{name}.bash +%endif %changelog +* Sat Jul 18 2020 JP Mens <j...@mens.de> 1.4 +- bump version -- see Changelog +* Tue Apr 28 2020 Christian Albrecht <c...@albix.de> 1.3-2 +- Fix broken download url +- Make bash completion work on RHEL based distros +* Tue Apr 7 2020 Kilian Cavalotti <kil...@stanford.edu> 1.3-1 +- Bumped to 1.3 release version +- Include bash-completion file in package * Thu May 18 2017 Fabian Arrotin <fabian.arro...@arrfab.net> 1.1-1 - Bumped to 1.1 release version * Wed Mar 15 2017 Fabian Arrotin <fabian.arro...@arrfab.net> 1.0-1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jo-1.3/snapcraft.yaml new/jo-1.4/snapcraft.yaml --- old/jo-1.3/snapcraft.yaml 2019-11-04 09:40:39.000000000 +0100 +++ new/jo-1.4/snapcraft.yaml 2020-07-18 18:20:38.000000000 +0200 @@ -1,11 +1,12 @@ name: jo -version: "1.3" +version: "1.4" summary: jo description: | This is jo, a small utility to create JSON objects or arrays. confinement: strict grade: stable +base: core apps: jo: @@ -17,3 +18,5 @@ plugin: autotools source-type: git source: https://github.com/jpmens/jo + build-packages: + - pkg-config diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jo-1.3/tests/jo.17.exp new/jo-1.4/tests/jo.17.exp --- old/jo-1.3/tests/jo.17.exp 2019-11-04 09:40:39.000000000 +0100 +++ new/jo-1.4/tests/jo.17.exp 2020-07-18 18:20:38.000000000 +0200 @@ -6,6 +6,10 @@ {"s":"false","n":0,"b":false,"a":false} {"s":"","n":0,"b":false,"a":null} ["123",14,true,456] +["-s",2,true] +["--test","--toast"] +{"--test":"--toast"} +[true,"--toast","--test","--toast",6,"--toast"] {"s":false,"n":false,"b":false,"a":false} {"s":true,"n":true,"b":true,"a":true} {"s":"Jan-Piet Mens <jpm...@gmail.com>","n":"Jan-Piet Mens <jpm...@gmail.com>","b":"Jan-Piet Mens <jpm...@gmail.com>","a":"Jan-Piet Mens <jpm...@gmail.com>"} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jo-1.3/tests/jo.17.sh new/jo-1.4/tests/jo.17.sh --- old/jo-1.3/tests/jo.17.sh 2019-11-04 09:40:39.000000000 +0100 +++ new/jo-1.4/tests/jo.17.sh 2020-07-18 18:20:38.000000000 +0200 @@ -8,6 +8,16 @@ # coerce array items ${JO:-jo} -a -- -s 123 -n "This is a test" -b C_Rocks 456 +# coercion flag strings should be usable as inputs, when they aren't flags +${JO:-jo} -a -- -s -s -n -n -b -b + +# non-flag strings should be read as normal strings, even if they begin with "-" +${JO:-jo} -a -- --test --toast +${JO:-jo} -- --test=--toast + +# coercion is one-shot, so all "--toast" strings are normal input +${JO:-jo} -a -- -b --test --toast -s --test --toast -n --test --toast + ### These should NOT be coerced # @ booleans