From 16a53e5bcbd970f845bd229fb0be261fd895d2e5 Mon Sep 17 00:00:00 2001 From: Joseph Werle Date: Thu, 22 May 2014 15:52:55 -0400 Subject: [PATCH] init --- Makefile | 0 bpkg | 1 + bpkg-install | 1 + bpkg-json | 1 + bpkg-package | 1 + bpkg.sh | 60 ++++++ lib/install/install.sh | 140 +++++++++++++ lib/json/.gitignore | 2 + lib/json/JSON.sh | 191 ++++++++++++++++++ lib/json/LICENSE.APACHE2 | 15 ++ lib/json/LICENSE.MIT | 24 +++ lib/json/README.md | 69 +++++++ lib/json/all-tests.sh | 30 +++ lib/json/package.json | 14 ++ lib/json/test/invalid-test.sh | 29 +++ .../test/invalid/bad_unicode_sequence.json | 1 + lib/json/test/invalid/bareword.json | 1 + lib/json/test/invalid/bracket_key.json | 1 + lib/json/test/invalid/colon.json | 1 + lib/json/test/invalid/colon_obj.json | 3 + lib/json/test/invalid/comma.json | 1 + lib/json/test/invalid/comma_obj.json | 3 + .../test/invalid/control_char_in_string.json | 1 + lib/json/test/invalid/decimal_point.json | 1 + lib/json/test/invalid/empty.json | 0 lib/json/test/invalid/false_key.json | 1 + lib/json/test/invalid/null_key.json | 1 + lib/json/test/invalid/number_key.json | 1 + .../test/invalid/trailing_array_comma.json | 1 + lib/json/test/invalid/trailing_garbage.json | 1 + .../test/invalid/trailing_object_comma.json | 1 + lib/json/test/invalid/true_key.json | 1 + lib/json/test/invalid/unclosed_array.json | 1 + lib/json/test/invalid/unclosed_object.json | 2 + lib/json/test/invalid/unclosed_string.json | 1 + lib/json/test/invalid/weird.json | 1 + lib/json/test/invalid/weird_key.json | 1 + lib/json/test/parse-test.sh | 35 ++++ lib/json/test/tokenizer-test.sh | 54 +++++ lib/json/test/valid-test.sh | 21 ++ lib/json/test/valid/array.json | 1 + lib/json/test/valid/array.parsed | 5 + lib/json/test/valid/embedded.json | 1 + lib/json/test/valid/embedded.parsed | 2 + lib/json/test/valid/empty_array.json | 1 + lib/json/test/valid/empty_array.parsed | 1 + lib/json/test/valid/empty_object.json | 1 + lib/json/test/valid/empty_object.parsed | 1 + lib/json/test/valid/many_object.json | 4 + lib/json/test/valid/many_object.parsed | 3 + lib/json/test/valid/nested_array.json | 5 + lib/json/test/valid/nested_array.parsed | 9 + lib/json/test/valid/nested_object.json | 7 + lib/json/test/valid/nested_object.parsed | 5 + lib/json/test/valid/number.json | 1 + lib/json/test/valid/number.parsed | 1 + lib/json/test/valid/object.json | 3 + lib/json/test/valid/object.parsed | 2 + lib/json/test/valid/string.json | 1 + lib/json/test/valid/string.parsed | 1 + lib/json/test/valid/string_in_array.json | 1 + lib/json/test/valid/string_in_array.parsed | 2 + lib/json/test/valid/string_in_object.json | 1 + lib/json/test/valid/string_in_object.parsed | 2 + lib/json/test/valid/tab_escape.json | 1 + lib/json/test/valid/tab_escape.parsed | 1 + lib/package/package.sh | 53 +++++ package.json | 9 + 68 files changed, 839 insertions(+) create mode 100644 Makefile create mode 120000 bpkg create mode 120000 bpkg-install create mode 120000 bpkg-json create mode 120000 bpkg-package create mode 100755 bpkg.sh create mode 100755 lib/install/install.sh create mode 100644 lib/json/.gitignore create mode 100755 lib/json/JSON.sh create mode 100644 lib/json/LICENSE.APACHE2 create mode 100644 lib/json/LICENSE.MIT create mode 100755 lib/json/README.md create mode 100755 lib/json/all-tests.sh create mode 100644 lib/json/package.json create mode 100755 lib/json/test/invalid-test.sh create mode 100644 lib/json/test/invalid/bad_unicode_sequence.json create mode 100644 lib/json/test/invalid/bareword.json create mode 100644 lib/json/test/invalid/bracket_key.json create mode 100644 lib/json/test/invalid/colon.json create mode 100644 lib/json/test/invalid/colon_obj.json create mode 100644 lib/json/test/invalid/comma.json create mode 100644 lib/json/test/invalid/comma_obj.json create mode 100644 lib/json/test/invalid/control_char_in_string.json create mode 100644 lib/json/test/invalid/decimal_point.json create mode 100644 lib/json/test/invalid/empty.json create mode 100644 lib/json/test/invalid/false_key.json create mode 100644 lib/json/test/invalid/null_key.json create mode 100644 lib/json/test/invalid/number_key.json create mode 100644 lib/json/test/invalid/trailing_array_comma.json create mode 100644 lib/json/test/invalid/trailing_garbage.json create mode 100644 lib/json/test/invalid/trailing_object_comma.json create mode 100644 lib/json/test/invalid/true_key.json create mode 100644 lib/json/test/invalid/unclosed_array.json create mode 100644 lib/json/test/invalid/unclosed_object.json create mode 100644 lib/json/test/invalid/unclosed_string.json create mode 100644 lib/json/test/invalid/weird.json create mode 100644 lib/json/test/invalid/weird_key.json create mode 100755 lib/json/test/parse-test.sh create mode 100755 lib/json/test/tokenizer-test.sh create mode 100755 lib/json/test/valid-test.sh create mode 100644 lib/json/test/valid/array.json create mode 100644 lib/json/test/valid/array.parsed create mode 100644 lib/json/test/valid/embedded.json create mode 100644 lib/json/test/valid/embedded.parsed create mode 100644 lib/json/test/valid/empty_array.json create mode 100644 lib/json/test/valid/empty_array.parsed create mode 100644 lib/json/test/valid/empty_object.json create mode 100644 lib/json/test/valid/empty_object.parsed create mode 100644 lib/json/test/valid/many_object.json create mode 100644 lib/json/test/valid/many_object.parsed create mode 100644 lib/json/test/valid/nested_array.json create mode 100644 lib/json/test/valid/nested_array.parsed create mode 100644 lib/json/test/valid/nested_object.json create mode 100644 lib/json/test/valid/nested_object.parsed create mode 100644 lib/json/test/valid/number.json create mode 100644 lib/json/test/valid/number.parsed create mode 100644 lib/json/test/valid/object.json create mode 100644 lib/json/test/valid/object.parsed create mode 100644 lib/json/test/valid/string.json create mode 100644 lib/json/test/valid/string.parsed create mode 100644 lib/json/test/valid/string_in_array.json create mode 100644 lib/json/test/valid/string_in_array.parsed create mode 100644 lib/json/test/valid/string_in_object.json create mode 100644 lib/json/test/valid/string_in_object.parsed create mode 100644 lib/json/test/valid/tab_escape.json create mode 100644 lib/json/test/valid/tab_escape.parsed create mode 100755 lib/package/package.sh create mode 100644 package.json diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e69de29 diff --git a/bpkg b/bpkg new file mode 120000 index 0000000..46262f6 --- /dev/null +++ b/bpkg @@ -0,0 +1 @@ +bpkg.sh \ No newline at end of file diff --git a/bpkg-install b/bpkg-install new file mode 120000 index 0000000..88e4e45 --- /dev/null +++ b/bpkg-install @@ -0,0 +1 @@ +lib/install/install.sh \ No newline at end of file diff --git a/bpkg-json b/bpkg-json new file mode 120000 index 0000000..4b3b259 --- /dev/null +++ b/bpkg-json @@ -0,0 +1 @@ +lib/json/JSON.sh \ No newline at end of file diff --git a/bpkg-package b/bpkg-package new file mode 120000 index 0000000..44b9fe6 --- /dev/null +++ b/bpkg-package @@ -0,0 +1 @@ +lib/package/package.sh \ No newline at end of file diff --git a/bpkg.sh b/bpkg.sh new file mode 100755 index 0000000..4d75bf3 --- /dev/null +++ b/bpkg.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +VERSION="0.0.1" + +## output error to stderr +error () { + printf >&2 "error: %s\n" "${@}" +} + +## output usage +usage () { + echo "usage: bpkg [-hV] [args]" +} + +## feature tests +features () { + if ! type bpkg-json > /dev/null 2>&1; then + error "Missing json parser dependency" + exit 1 + fi +} + +bpkg () { + local arg="$1" + local cmd="" + shift + + case "${arg}" in + + ## flags + -V|--version) + echo "${VERSION}" + return 0 + ;; + + -h|--help) + usage + return 0 + ;; + + *) + cmd="bpkg-${arg}" + if type -f "${cmd}" > /dev/null 2>&1; then + "${cmd}" "${@}" + return $? + fi + ;; + + esac +} + +## test for required features +features + +if [[ ${BASH_SOURCE[0]} != $0 ]]; then + export -f bpkg +else + bpkg "${@}" + exit $? +fi diff --git a/lib/install/install.sh b/lib/install/install.sh new file mode 100755 index 0000000..51f1114 --- /dev/null +++ b/lib/install/install.sh @@ -0,0 +1,140 @@ +#!/bin/bash + +BPKG_REMOTE="${BPKG_REMOTE:-"https://raw.githubusercontent.com"}" +BPKG_USER="${BPKG_USER:-"bpkg"}" + +## outut usage +usage () { + echo "usage: bpkg-install [-h|--help]" + echo " or: bpkg-install " + echo " or: bpkg-install /" +} + +## Install a bash package +bpkg_install () { + local pkg="${1}" + local cwd="`pwd`" + local user="" + local name="" + local url="" + local uri="" + local version="" + local status="" + local json="" + declare -a local parts=() + declare -a local scripts=() + + case "${pkg}" in + -h|--help) + usage + return 0 + ;; + esac + + ## ensure there is a package to install + if [ -z "${pkg}" ]; then + return 1 + fi + + ## ensure remote is reachable + { + curl -s "${BPKG_REMOTE}" + if [ "0" != "$?" ]; then + return 1 + fi + } + + ## get version if available + { + OLDIFS="${IFS}" + IFS="@" + parts=(${pkg}) + IFS="${OLDIFS}" + } + + if [ "1" = "${#parts[@]}" ]; then + version="master" + elif [ "2" = "${#parts[@]}" ]; then + name="${parts[0]}" + version="${parts[1]}" + else + echo >&2 "error: Error parsing package version" + return 1 + fi + + ## split by user name and repo + { + OLDIFS="${IFS}" + IFS='/' + parts=(${pkg}) + IFS="${OLDIFS}" + } + + if [ "1" = "${#parts[@]}" ]; then + user="${BPKG_USER}" + name="${parts[0]}" + elif [ "2" = "${#parts[@]}" ]; then + user="${parts[0]}" + name="${parts[1]}" + else + echo >&2 "error: Unable to determine package name" + return 1 + fi + + ## clean up name of weird trailing versions + name=${name/@*//} + + ## build uri portion + uri="/${user}/${name}/${version}" + + ## clean up extra slashes in uri + uri=${uri/\/\///} + + ## build url + url="${BPKG_REMOTE}${uri}" + + ## determine if `package.json' exists at url + { + status=$(curl -sL "${url}/package.json" -w '%{http_code}' -o /dev/null) + if [ "0" != "$?" ] || (( status >= 400 )); then + echo >&2 "error: Package doesn't exist" + return 1 + fi + } + + ## read package.json + json=$(curl -sL "${url}/package.json") + + ## construct scripts array + { + scripts=$(echo -n $json | bpkg-json -b | grep 'scripts' | awk '{ print $2 }' | tr -d '"') + OLDIFS="${IFS}" + IFS=',' + scripts=($(echo ${scripts})) + IFS="${OLDIFS}" + } + + ## get package name from `package.json' + name="$(echo -n ${json} | bpkg-json -b | grep 'name' | awk '{ print $2 }' | tr -d '"')" + + if [ "${#scripts[@]}" -gt "0" ]; then + ## make `deps/' directory if possible + mkdir -p "${cwd}/deps/${name}" + ## grab each script and place in deps directory + for (( i = 0; i < ${#scripts[@]} ; ++i )); do + ( + local script=${scripts[$i]} + curl -sL "${url}/${script}" -o "${cwd}/deps/${name}/${script}" + ) + done + fi + + return 0 +} + +if [[ ${BASH_SOURCE[0]} != $0 ]]; then + export -f bpkg_install +else + bpkg_install "${@}" + exit $? +fi diff --git a/lib/json/.gitignore b/lib/json/.gitignore new file mode 100644 index 0000000..05269a5 --- /dev/null +++ b/lib/json/.gitignore @@ -0,0 +1,2 @@ +test/errlog +test/outlog diff --git a/lib/json/JSON.sh b/lib/json/JSON.sh new file mode 100755 index 0000000..e5d9ac5 --- /dev/null +++ b/lib/json/JSON.sh @@ -0,0 +1,191 @@ +#!/usr/bin/env bash + +throw () { + echo "$*" >&2 + exit 1 +} + +BRIEF=0 +LEAFONLY=0 +PRUNE=0 + +usage() { + echo + echo "Usage: JSON.sh [-b] [-l] [-p] [-h]" + echo + echo "-p - Prune empty. Exclude fields with empty values." + echo "-l - Leaf only. Only show leaf nodes, which stops data duplication." + echo "-b - Brief. Combines 'Leaf only' and 'Prune empty' options." + echo "-h - This help text." + echo +} + +parse_options() { + set -- "$@" + local ARGN=$# + while [ $ARGN -ne 0 ] + do + case $1 in + -h) usage + exit 0 + ;; + -b) BRIEF=1 + LEAFONLY=1 + PRUNE=1 + ;; + -l) LEAFONLY=1 + ;; + -p) PRUNE=1 + ;; + ?*) echo "ERROR: Unknown option." + usage + exit 0 + ;; + esac + shift 1 + ARGN=$((ARGN-1)) + done +} + +awk_egrep () { + local pattern_string=$1 + + gawk '{ + while ($0) { + start=match($0, pattern); + token=substr($0, start, RLENGTH); + print token; + $0=substr($0, start+RLENGTH); + } + }' pattern=$pattern_string +} + +tokenize () { + local GREP + local ESCAPE + local CHAR + + if echo "test string" | egrep -ao --color=never "test" &>/dev/null + then + GREP='egrep -ao --color=never' + else + GREP='egrep -ao' + fi + + if echo "test string" | egrep -o "test" &>/dev/null + then + ESCAPE='(\\[^u[:cntrl:]]|\\u[0-9a-fA-F]{4})' + CHAR='[^[:cntrl:]"\\]' + else + GREP=awk_egrep + ESCAPE='(\\\\[^u[:cntrl:]]|\\u[0-9a-fA-F]{4})' + CHAR='[^[:cntrl:]"\\\\]' + fi + + local STRING="\"$CHAR*($ESCAPE$CHAR*)*\"" + local NUMBER='-?(0|[1-9][0-9]*)([.][0-9]*)?([eE][+-]?[0-9]*)?' + local KEYWORD='null|false|true' + local SPACE='[[:space:]]+' + + $GREP "$STRING|$NUMBER|$KEYWORD|$SPACE|." | egrep -v "^$SPACE$" +} + +parse_array () { + local index=0 + local ary='' + read -r token + case "$token" in + ']') ;; + *) + while : + do + parse_value "$1" "$index" + index=$((index+1)) + ary="$ary""$value" + read -r token + case "$token" in + ']') break ;; + ',') ary="$ary," ;; + *) throw "EXPECTED , or ] GOT ${token:-EOF}" ;; + esac + read -r token + done + ;; + esac + [ "$BRIEF" -eq 0 ] && value=`printf '[%s]' "$ary"` || value= + : +} + +parse_object () { + local key + local obj='' + read -r token + case "$token" in + '}') ;; + *) + while : + do + case "$token" in + '"'*'"') key=$token ;; + *) throw "EXPECTED string GOT ${token:-EOF}" ;; + esac + read -r token + case "$token" in + ':') ;; + *) throw "EXPECTED : GOT ${token:-EOF}" ;; + esac + read -r token + parse_value "$1" "$key" + obj="$obj$key:$value" + read -r token + case "$token" in + '}') break ;; + ',') obj="$obj," ;; + *) throw "EXPECTED , or } GOT ${token:-EOF}" ;; + esac + read -r token + done + ;; + esac + [ "$BRIEF" -eq 0 ] && value=`printf '{%s}' "$obj"` || value= + : +} + +parse_value () { + local jpath="${1:+$1,}$2" isleaf=0 isempty=0 print=0 + case "$token" in + '{') parse_object "$jpath" ;; + '[') parse_array "$jpath" ;; + # At this point, the only valid single-character tokens are digits. + ''|[!0-9]) throw "EXPECTED value GOT ${token:-EOF}" ;; + *) value=$token + isleaf=1 + [ "$value" = '""' ] && isempty=1 + ;; + esac + [ "$value" = '' ] && return + [ "$LEAFONLY" -eq 0 ] && [ "$PRUNE" -eq 0 ] && print=1 + [ "$LEAFONLY" -eq 1 ] && [ "$isleaf" -eq 1 ] && [ $PRUNE -eq 0 ] && print=1 + [ "$LEAFONLY" -eq 0 ] && [ "$PRUNE" -eq 1 ] && [ "$isempty" -eq 0 ] && print=1 + [ "$LEAFONLY" -eq 1 ] && [ "$isleaf" -eq 1 ] && \ + [ $PRUNE -eq 1 ] && [ $isempty -eq 0 ] && print=1 + [ "$print" -eq 1 ] && printf "[%s]\t%s\n" "$jpath" "$value" + : +} + +parse () { + read -r token + parse_value + read -r token + case "$token" in + '') ;; + *) throw "EXPECTED EOF GOT $token" ;; + esac +} + +parse_options "$@" + +if ([ "$0" = "$BASH_SOURCE" ] || ! [ -n "$BASH_SOURCE" ]); +then + tokenize | parse +fi diff --git a/lib/json/LICENSE.APACHE2 b/lib/json/LICENSE.APACHE2 new file mode 100644 index 0000000..6366c04 --- /dev/null +++ b/lib/json/LICENSE.APACHE2 @@ -0,0 +1,15 @@ +Apache License, Version 2.0 + +Copyright (c) 2011 Dominic Tarr + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/lib/json/LICENSE.MIT b/lib/json/LICENSE.MIT new file mode 100644 index 0000000..6eafbd7 --- /dev/null +++ b/lib/json/LICENSE.MIT @@ -0,0 +1,24 @@ +The MIT License + +Copyright (c) 2011 Dominic Tarr + +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/lib/json/README.md b/lib/json/README.md new file mode 100755 index 0000000..d5b4683 --- /dev/null +++ b/lib/json/README.md @@ -0,0 +1,69 @@ +# JSON.sh + +yo, so it's a json parser written in bash + +pipe json to it, and it traverses the json objects and prints out the +path to the current object (as a JSON array) and then the object, without whitespace. + +``` bash +$ json_parse < package.json +["name"] "JSON.sh" +["version"] "0.0.0" +["description"] "" +["homepage"] "http://github.com/dominictarr/JSON.sh" +["repository","type"] "git" +["repository","url"] "https://github.com/dominictarr/JSON.sh.git" +["repository"] {"type":"git","url":"https://github.com/dominictarr/JSON.sh.git"} +["bin","json_parse"] "./JSON.sh" +["bin"] {"json_parse":"./JSON.sh"} +["dependencies"] {} +# ... etc +``` + +a more complex example: + +``` bash +curl registry.npmjs.org/express | ./JSON.sh | egrep '\["versions","[^"]*"\]' +... try it and see +``` + +## Options + +-b +> Brief output. Combines 'Leaf only' and 'Prune empty' options. + +-l +> Leaf only. Only show leaf nodes, which stops data duplication. + +-p +> Prune empty. Exclude fields with empty values. + +-h +> Show help text. + +## Cool Links + +* [step-/JSON.awk](https://github.com/step-/JSON.awk) JSON.sh ported to awk +* [kristopolous/TickTick](https://github.com/kristopolous/TickTick) Object Oriented BASH +* [archan937/jsonv.sh](https://github.com/archan937/jsonv.sh) + +## Examples + +If you have any examples with JSON.sh, streaming twitter, github, or whatever! +please issue a pull request and i will include them. + +## Installation + +install via npm or from AUR on archlinux + +* `npm install -g JSON.sh` +* `yaourt -Sy json-sh` + ([json-sh on aur](https://aur.archlinux.org/packages/json-sh/) + thanks to [kremlin-](https://github.com/kremlin-)) + +## License + +This software is available under the following licenses: + + * MIT + * Apache 2 diff --git a/lib/json/all-tests.sh b/lib/json/all-tests.sh new file mode 100755 index 0000000..9aab42d --- /dev/null +++ b/lib/json/all-tests.sh @@ -0,0 +1,30 @@ +#! /usr/bin/env bash + +cd ${0%/*} + +#set -e +fail=0 +tests=0 +#all_tests=${__dirname:} +#echo PLAN ${#all_tests} +for test in test/*.sh ; +do + tests=$((tests+1)) + echo TEST: $test + ./$test + ret=$? + if [ $ret -eq 0 ] ; then + echo OK: ---- $test + passed=$((passed+1)) + else + echo FAIL: $test $fail + fail=$((fail+ret)) + fi +done + +if [ $fail -eq 0 ]; then + echo -n 'SUCCESS ' +else + echo -n 'FAILURE ' +fi +echo $passed / $tests diff --git a/lib/json/package.json b/lib/json/package.json new file mode 100644 index 0000000..8d73d02 --- /dev/null +++ b/lib/json/package.json @@ -0,0 +1,14 @@ +{ "name": "JSON.sh" +, "version": "0.1.8" +, "description": "JSON parser written in bash" +, "homepage": "http://github.com/dominictarr/JSON.sh" +, "repository": + { "type": "git" + , "url": "https://github.com/dominictarr/JSON.sh.git" } +, "bin": { + "JSON.sh": "./JSON.sh" + } +, "dependencies": {} +, "devDependencies": {} +, "author": "Dominic Tarr (http://bit.ly/dominictarr)" +, "scripts": { "test": "./all-tests.sh" } } diff --git a/lib/json/test/invalid-test.sh b/lib/json/test/invalid-test.sh new file mode 100755 index 0000000..b2350ec --- /dev/null +++ b/lib/json/test/invalid-test.sh @@ -0,0 +1,29 @@ +#! /usr/bin/env bash + +cd ${0%/*} + +# make test output TAP compatible +# http://en.wikipedia.org/wiki/Test_Anything_Protocol + +fails=0 +tests=`ls invalid/* -1 | wc -l` + +echo "1..${tests##* }" +for input in invalid/* +do + i=$((i+1)) + if ../JSON.sh < "$input" > /tmp/JSON.sh_outlog 2> /tmp/JSON.sh_errlog + then + echo "not ok $i - cat $input | ../JSON.sh should fail" + #this should be indented with '#' at the start. + echo "OUTPUT WAS >>>" + cat /tmp/JSON.sh_outlog + echo "<<<" + fails=$((fails+1)) + else + echo "ok $i - $input was rejected" + echo "#" `cat /tmp/JSON.sh_errlog` + fi +done +echo "$fails test(s) failed" +exit $fails diff --git a/lib/json/test/invalid/bad_unicode_sequence.json b/lib/json/test/invalid/bad_unicode_sequence.json new file mode 100644 index 0000000..a3cd4da --- /dev/null +++ b/lib/json/test/invalid/bad_unicode_sequence.json @@ -0,0 +1 @@ +"hello\u20world" diff --git a/lib/json/test/invalid/bareword.json b/lib/json/test/invalid/bareword.json new file mode 100644 index 0000000..4d75b2d --- /dev/null +++ b/lib/json/test/invalid/bareword.json @@ -0,0 +1 @@ +bareword diff --git a/lib/json/test/invalid/bracket_key.json b/lib/json/test/invalid/bracket_key.json new file mode 100644 index 0000000..684de58 --- /dev/null +++ b/lib/json/test/invalid/bracket_key.json @@ -0,0 +1 @@ +{[: "bad"} diff --git a/lib/json/test/invalid/colon.json b/lib/json/test/invalid/colon.json new file mode 100644 index 0000000..397db75 --- /dev/null +++ b/lib/json/test/invalid/colon.json @@ -0,0 +1 @@ +: diff --git a/lib/json/test/invalid/colon_obj.json b/lib/json/test/invalid/colon_obj.json new file mode 100644 index 0000000..9380111 --- /dev/null +++ b/lib/json/test/invalid/colon_obj.json @@ -0,0 +1,3 @@ +{ + "hello": : +} diff --git a/lib/json/test/invalid/comma.json b/lib/json/test/invalid/comma.json new file mode 100644 index 0000000..9d7077c --- /dev/null +++ b/lib/json/test/invalid/comma.json @@ -0,0 +1 @@ +[,] \ No newline at end of file diff --git a/lib/json/test/invalid/comma_obj.json b/lib/json/test/invalid/comma_obj.json new file mode 100644 index 0000000..b9de03a --- /dev/null +++ b/lib/json/test/invalid/comma_obj.json @@ -0,0 +1,3 @@ +{ + "hello", "comma!" +} \ No newline at end of file diff --git a/lib/json/test/invalid/control_char_in_string.json b/lib/json/test/invalid/control_char_in_string.json new file mode 100644 index 0000000..8fdb891 --- /dev/null +++ b/lib/json/test/invalid/control_char_in_string.json @@ -0,0 +1 @@ +"ab" diff --git a/lib/json/test/invalid/decimal_point.json b/lib/json/test/invalid/decimal_point.json new file mode 100644 index 0000000..9c558e3 --- /dev/null +++ b/lib/json/test/invalid/decimal_point.json @@ -0,0 +1 @@ +. diff --git a/lib/json/test/invalid/empty.json b/lib/json/test/invalid/empty.json new file mode 100644 index 0000000..e69de29 diff --git a/lib/json/test/invalid/false_key.json b/lib/json/test/invalid/false_key.json new file mode 100644 index 0000000..3541e4a --- /dev/null +++ b/lib/json/test/invalid/false_key.json @@ -0,0 +1 @@ +{false: "bad"} diff --git a/lib/json/test/invalid/null_key.json b/lib/json/test/invalid/null_key.json new file mode 100644 index 0000000..6c2e714 --- /dev/null +++ b/lib/json/test/invalid/null_key.json @@ -0,0 +1 @@ +{null: "bad"} diff --git a/lib/json/test/invalid/number_key.json b/lib/json/test/invalid/number_key.json new file mode 100644 index 0000000..d0daab3 --- /dev/null +++ b/lib/json/test/invalid/number_key.json @@ -0,0 +1 @@ +{5: "bad"} diff --git a/lib/json/test/invalid/trailing_array_comma.json b/lib/json/test/invalid/trailing_array_comma.json new file mode 100644 index 0000000..e2ae501 --- /dev/null +++ b/lib/json/test/invalid/trailing_array_comma.json @@ -0,0 +1 @@ +[1,2,3,] diff --git a/lib/json/test/invalid/trailing_garbage.json b/lib/json/test/invalid/trailing_garbage.json new file mode 100644 index 0000000..33becc9 --- /dev/null +++ b/lib/json/test/invalid/trailing_garbage.json @@ -0,0 +1 @@ +[1,2,3]' diff --git a/lib/json/test/invalid/trailing_object_comma.json b/lib/json/test/invalid/trailing_object_comma.json new file mode 100644 index 0000000..71d0f1b --- /dev/null +++ b/lib/json/test/invalid/trailing_object_comma.json @@ -0,0 +1 @@ +{"a":1,"b":2,"c":3,} diff --git a/lib/json/test/invalid/true_key.json b/lib/json/test/invalid/true_key.json new file mode 100644 index 0000000..093306f --- /dev/null +++ b/lib/json/test/invalid/true_key.json @@ -0,0 +1 @@ +{true: "bad"} diff --git a/lib/json/test/invalid/unclosed_array.json b/lib/json/test/invalid/unclosed_array.json new file mode 100644 index 0000000..1fadb02 --- /dev/null +++ b/lib/json/test/invalid/unclosed_array.json @@ -0,0 +1 @@ +[5,4,"goeu" \ No newline at end of file diff --git a/lib/json/test/invalid/unclosed_object.json b/lib/json/test/invalid/unclosed_object.json new file mode 100644 index 0000000..aaceb30 --- /dev/null +++ b/lib/json/test/invalid/unclosed_object.json @@ -0,0 +1,2 @@ +{ + "hELLO": "goodeoeu" \ No newline at end of file diff --git a/lib/json/test/invalid/unclosed_string.json b/lib/json/test/invalid/unclosed_string.json new file mode 100644 index 0000000..8e9add3 --- /dev/null +++ b/lib/json/test/invalid/unclosed_string.json @@ -0,0 +1 @@ +"Hello world diff --git a/lib/json/test/invalid/weird.json b/lib/json/test/invalid/weird.json new file mode 100644 index 0000000..59c227c --- /dev/null +++ b/lib/json/test/invalid/weird.json @@ -0,0 +1 @@ +@ diff --git a/lib/json/test/invalid/weird_key.json b/lib/json/test/invalid/weird_key.json new file mode 100644 index 0000000..c53a30d --- /dev/null +++ b/lib/json/test/invalid/weird_key.json @@ -0,0 +1 @@ +{@: "bad"} diff --git a/lib/json/test/parse-test.sh b/lib/json/test/parse-test.sh new file mode 100755 index 0000000..94d0195 --- /dev/null +++ b/lib/json/test/parse-test.sh @@ -0,0 +1,35 @@ +#! /usr/bin/env bash + +cd ${0%/*} + +. ../JSON.sh + +ptest () { + tokenize | parse >/dev/null +} + +fails=0 +i=0 +echo "1..4" +for input in '"oooo" ' '[true, 1, [0, {}]] ' '{"true": 1}' +do + i=$((i+1)) + if echo "$input" | ptest + then + echo "ok $i - $input" + else + echo "not ok $i - $input" + fails=$((fails+1)) + fi +done + +if ! ptest < ../package.json +then + echo "not ok 4 - Parsing package.json failed!" + fails=$((fails+1)) +else + echo "ok $i - package.json" +fi + +echo "$fails test(s) failed" +exit $fails diff --git a/lib/json/test/tokenizer-test.sh b/lib/json/test/tokenizer-test.sh new file mode 100755 index 0000000..4d5988c --- /dev/null +++ b/lib/json/test/tokenizer-test.sh @@ -0,0 +1,54 @@ +#! /usr/bin/env bash + +cd ${0%/*} +. ../JSON.sh + +i=0 +fails=0 +ttest () { + i=$((i+1)) + local input="$1"; shift + local expected="$(printf '%s\n' "$@")" + echo "$expected" > /tmp/json_ttest_expected + if echo "$input" | tokenize | diff -u - /tmp/json_ttest_expected + then + echo "ok $i - $input" + else + echo "not ok $i - $input" + fails=$((fails+1)) + fi +} + +ttest '"dah"' '"dah"' +ttest '""' '""' +ttest '["dah"]' '[' '"dah"' ']' +ttest '" "' '" "' +ttest '" \" "' '" \" "' + +ttest '["dah"]' '[' '"dah"' ']' + +ttest '123' '123' +ttest '123.142' '123.142' +ttest '-123' '-123' + +ttest '1e23' '1e23' +ttest '0.1' '0.1' +ttest '-110' '-110' +ttest '-110.10' '-110.10' +ttest '-110e10' '-110e10' +ttest 'null' 'null' +ttest 'true' 'true' +ttest 'false' 'false' +ttest '[ null , -110e10, "null" ]' \ + '[' 'null' ',' '-110e10' ',' '"null"' ']' +ttest '{"e": false}' '{' '"e"' ':' 'false' '}' +ttest '{"e": "string"}' '{' '"e"' ':' '"string"' '}' + +if ! cat ../package.json | tokenize >/dev/null +then + fails=$((fails+1)) + echo "Tokenizing package.json failed!" +fi + +echo "$fails test(s) failed" +exit $fails diff --git a/lib/json/test/valid-test.sh b/lib/json/test/valid-test.sh new file mode 100755 index 0000000..cad183b --- /dev/null +++ b/lib/json/test/valid-test.sh @@ -0,0 +1,21 @@ +#! /usr/bin/env bash + +cd ${0%/*} +fails=0 +i=0 +tests=`ls valid/*.json -1l | wc -l` +echo "1..$tests" +for input in valid/*.json +do + expected="${input%.json}.parsed" + i=$((i+1)) + if ! ../JSON.sh < "$input" | diff -u - "$expected" + then + echo "not ok $i - $input" + fails=$((fails+1)) + else + echo "ok $i - $input" + fi +done +echo "$fails test(s) failed" +exit $fails diff --git a/lib/json/test/valid/array.json b/lib/json/test/valid/array.json new file mode 100644 index 0000000..3a91d37 --- /dev/null +++ b/lib/json/test/valid/array.json @@ -0,0 +1 @@ +[1,2,3,"hello"] \ No newline at end of file diff --git a/lib/json/test/valid/array.parsed b/lib/json/test/valid/array.parsed new file mode 100644 index 0000000..d564cd9 --- /dev/null +++ b/lib/json/test/valid/array.parsed @@ -0,0 +1,5 @@ +[0] 1 +[1] 2 +[2] 3 +[3] "hello" +[] [1,2,3,"hello"] diff --git a/lib/json/test/valid/embedded.json b/lib/json/test/valid/embedded.json new file mode 100644 index 0000000..ddcdfb9 --- /dev/null +++ b/lib/json/test/valid/embedded.json @@ -0,0 +1 @@ +{"foo": "{\"foo\":\"bar\"}"} diff --git a/lib/json/test/valid/embedded.parsed b/lib/json/test/valid/embedded.parsed new file mode 100644 index 0000000..041eaf9 --- /dev/null +++ b/lib/json/test/valid/embedded.parsed @@ -0,0 +1,2 @@ +["foo"] "{\"foo\":\"bar\"}" +[] {"foo":"{\"foo\":\"bar\"}"} diff --git a/lib/json/test/valid/empty_array.json b/lib/json/test/valid/empty_array.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/lib/json/test/valid/empty_array.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/lib/json/test/valid/empty_array.parsed b/lib/json/test/valid/empty_array.parsed new file mode 100644 index 0000000..d24d150 --- /dev/null +++ b/lib/json/test/valid/empty_array.parsed @@ -0,0 +1 @@ +[] [] diff --git a/lib/json/test/valid/empty_object.json b/lib/json/test/valid/empty_object.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/lib/json/test/valid/empty_object.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/lib/json/test/valid/empty_object.parsed b/lib/json/test/valid/empty_object.parsed new file mode 100644 index 0000000..4cdea2a --- /dev/null +++ b/lib/json/test/valid/empty_object.parsed @@ -0,0 +1 @@ +[] {} diff --git a/lib/json/test/valid/many_object.json b/lib/json/test/valid/many_object.json new file mode 100644 index 0000000..214a040 --- /dev/null +++ b/lib/json/test/valid/many_object.json @@ -0,0 +1,4 @@ +{ + "key1": "string", + "key2": 3573 +} \ No newline at end of file diff --git a/lib/json/test/valid/many_object.parsed b/lib/json/test/valid/many_object.parsed new file mode 100644 index 0000000..fd1fccb --- /dev/null +++ b/lib/json/test/valid/many_object.parsed @@ -0,0 +1,3 @@ +["key1"] "string" +["key2"] 3573 +[] {"key1":"string","key2":3573} diff --git a/lib/json/test/valid/nested_array.json b/lib/json/test/valid/nested_array.json new file mode 100644 index 0000000..819b356 --- /dev/null +++ b/lib/json/test/valid/nested_array.json @@ -0,0 +1,5 @@ +[ 1 +, [] +, [4, "hello", {}] +, {"array": []} +] \ No newline at end of file diff --git a/lib/json/test/valid/nested_array.parsed b/lib/json/test/valid/nested_array.parsed new file mode 100644 index 0000000..4638c15 --- /dev/null +++ b/lib/json/test/valid/nested_array.parsed @@ -0,0 +1,9 @@ +[0] 1 +[1] [] +[2,0] 4 +[2,1] "hello" +[2,2] {} +[2] [4,"hello",{}] +[3,"array"] [] +[3] {"array":[]} +[] [1,[],[4,"hello",{}],{"array":[]}] diff --git a/lib/json/test/valid/nested_object.json b/lib/json/test/valid/nested_object.json new file mode 100644 index 0000000..37e3bc7 --- /dev/null +++ b/lib/json/test/valid/nested_object.json @@ -0,0 +1,7 @@ +{ + "object": { + "key": "value", + "empty": {} + }, + "number": 5 +} \ No newline at end of file diff --git a/lib/json/test/valid/nested_object.parsed b/lib/json/test/valid/nested_object.parsed new file mode 100644 index 0000000..8609e30 --- /dev/null +++ b/lib/json/test/valid/nested_object.parsed @@ -0,0 +1,5 @@ +["object","key"] "value" +["object","empty"] {} +["object"] {"key":"value","empty":{}} +["number"] 5 +[] {"object":{"key":"value","empty":{}},"number":5} diff --git a/lib/json/test/valid/number.json b/lib/json/test/valid/number.json new file mode 100644 index 0000000..e440e5c --- /dev/null +++ b/lib/json/test/valid/number.json @@ -0,0 +1 @@ +3 \ No newline at end of file diff --git a/lib/json/test/valid/number.parsed b/lib/json/test/valid/number.parsed new file mode 100644 index 0000000..2a1fecb --- /dev/null +++ b/lib/json/test/valid/number.parsed @@ -0,0 +1 @@ +[] 3 diff --git a/lib/json/test/valid/object.json b/lib/json/test/valid/object.json new file mode 100644 index 0000000..932512e --- /dev/null +++ b/lib/json/test/valid/object.json @@ -0,0 +1,3 @@ +{ + "key": "Value" +} \ No newline at end of file diff --git a/lib/json/test/valid/object.parsed b/lib/json/test/valid/object.parsed new file mode 100644 index 0000000..9f50711 --- /dev/null +++ b/lib/json/test/valid/object.parsed @@ -0,0 +1,2 @@ +["key"] "Value" +[] {"key":"Value"} diff --git a/lib/json/test/valid/string.json b/lib/json/test/valid/string.json new file mode 100644 index 0000000..2a18e8a --- /dev/null +++ b/lib/json/test/valid/string.json @@ -0,0 +1 @@ +"hello this is a string" \ No newline at end of file diff --git a/lib/json/test/valid/string.parsed b/lib/json/test/valid/string.parsed new file mode 100644 index 0000000..b1fb986 --- /dev/null +++ b/lib/json/test/valid/string.parsed @@ -0,0 +1 @@ +[] "hello this is a string" diff --git a/lib/json/test/valid/string_in_array.json b/lib/json/test/valid/string_in_array.json new file mode 100644 index 0000000..ace1d0f --- /dev/null +++ b/lib/json/test/valid/string_in_array.json @@ -0,0 +1 @@ +["hello this is a string"] \ No newline at end of file diff --git a/lib/json/test/valid/string_in_array.parsed b/lib/json/test/valid/string_in_array.parsed new file mode 100644 index 0000000..a49e6d9 --- /dev/null +++ b/lib/json/test/valid/string_in_array.parsed @@ -0,0 +1,2 @@ +[0] "hello this is a string" +[] ["hello this is a string"] diff --git a/lib/json/test/valid/string_in_object.json b/lib/json/test/valid/string_in_object.json new file mode 100644 index 0000000..4f84650 --- /dev/null +++ b/lib/json/test/valid/string_in_object.json @@ -0,0 +1 @@ +{"key":"hello this is a string"} \ No newline at end of file diff --git a/lib/json/test/valid/string_in_object.parsed b/lib/json/test/valid/string_in_object.parsed new file mode 100644 index 0000000..e266552 --- /dev/null +++ b/lib/json/test/valid/string_in_object.parsed @@ -0,0 +1,2 @@ +["key"] "hello this is a string" +[] {"key":"hello this is a string"} diff --git a/lib/json/test/valid/tab_escape.json b/lib/json/test/valid/tab_escape.json new file mode 100644 index 0000000..b7e42b8 --- /dev/null +++ b/lib/json/test/valid/tab_escape.json @@ -0,0 +1 @@ +"hello\tworld" diff --git a/lib/json/test/valid/tab_escape.parsed b/lib/json/test/valid/tab_escape.parsed new file mode 100644 index 0000000..ee69dd9 --- /dev/null +++ b/lib/json/test/valid/tab_escape.parsed @@ -0,0 +1 @@ +[] "hello\tworld" diff --git a/lib/package/package.sh b/lib/package/package.sh new file mode 100755 index 0000000..dc4236f --- /dev/null +++ b/lib/package/package.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +## output usage +usage () { + echo "usage: bpkg-package [-h|--help]" + echo " or: bpkg-package " + echo " or: bpkg-package" +} + +## Read a package property +bpkg_package () { + local prop="${1}" + local cwd="`pwd`" + local pkg="${cwd}/package.json" + + ## parse flags + case "${prop}" in + -h|--help) + usage + return 0 + ;; + esac + + ## ensure there is a package to read + if ! test -f "${pkg}"; then + echo 2>&1 "error: Unable to find \`package.json' in `pwd`" + return 1 + fi + + if [ -z "${prop}" ]; then + ## output all propertyies if property + ## is ommited + { + cat "${pkg}" | bpkg-json -b + } + else + ## show value for a specific property + ## in `package.json' + { + cat "${pkg}" | bpkg-json -b | grep "${prop}" | awk '{ printf $2 }' + echo + } + fi + + return 0 +} + +if [[ ${BASH_SOURCE[0]} != $0 ]]; then + export -f bpkg_package +else + bpkg_package "${@}" + exit $? +fi diff --git a/package.json b/package.json new file mode 100644 index 0000000..133d9e9 --- /dev/null +++ b/package.json @@ -0,0 +1,9 @@ +{ + "name": "bpkg", + "version": "0.0.1", + "description": "Lightweight bash package manager", + "global": true, + "scripts": [ "bpkg.sh" ], + "bin": { "bpkg": "bpkg.sh" }, + "dependencies": { } +}