Compare commits
123
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0673dee1b3 | ||
|
|
3dc5f4e948 | ||
|
|
90bc29c1b5 | ||
|
|
7fd9e86ea6 | ||
|
|
e113c67d51 | ||
|
|
bf88c73c20 | ||
|
|
ae11f111a5 | ||
|
|
2ce1e323df | ||
|
|
68d0048148 | ||
|
|
bafb43e589 | ||
|
|
c163e88af3 | ||
|
|
bd34b8dd2e | ||
|
|
61edf3fa93 | ||
|
|
8eff744eec | ||
|
|
b4742c1257 | ||
|
|
9a4576e756 | ||
|
|
b5560d8420 | ||
|
|
79ece35981 | ||
|
|
f06deb828a | ||
|
|
0c9eacedaa | ||
|
|
476b36770d | ||
|
|
239278fd3a | ||
|
|
98b36e74b8 | ||
|
|
c3cbbdd8a2 | ||
|
|
18879fb993 | ||
|
|
bdaddcf107 | ||
|
|
7b81a836c3 | ||
|
|
c538237f4e | ||
|
|
4a6241be06 | ||
|
|
1900c7bcac | ||
|
|
578d536233 | ||
|
|
b0d6d28310 | ||
|
|
10d66051ef | ||
|
|
cb3d5aff84 | ||
|
|
ca12bd9b5d | ||
|
|
6d89e297fe | ||
|
|
32d8f2000a | ||
|
|
0bd7614bd5 | ||
|
|
4b4cf789e0 | ||
|
|
76b1fc18f1 | ||
|
|
405071337d | ||
|
|
151b494382 | ||
|
|
12f74b226e | ||
|
|
18753cb7a7 | ||
|
|
71d4ff6ed8 | ||
|
|
7935d307f0 | ||
|
|
38bfec1594 | ||
|
|
2421dfb843 | ||
|
|
94035be3fa | ||
|
|
c8a2a0acc2 | ||
|
|
3a8c8f4747 | ||
|
|
b142d484d5 | ||
|
|
674e9fb7c2 | ||
|
|
6878123202 | ||
|
|
f7bf9a8e96 | ||
|
|
89809a2d0f | ||
|
|
9df19f53e4 | ||
|
|
65cbaac344 | ||
|
|
2fcb257168 | ||
|
|
63791b795a | ||
|
|
ea90b947b0 | ||
|
|
9a0d796298 | ||
|
|
0337027d54 | ||
|
|
8ea21a54ad | ||
|
|
6bac4ed059 | ||
|
|
a03ae02f44 | ||
|
|
45f126514c | ||
|
|
b279713e47 | ||
|
|
3504bd9a2a | ||
|
|
1a8a0ae0f5 | ||
|
|
441b066dd8 | ||
|
|
ef87010059 | ||
|
|
66fb962a66 | ||
|
|
e8abc0a2f6 | ||
|
|
7b1bd99de6 | ||
|
|
8619c6e060 | ||
|
|
2e1f2db8b5 | ||
|
|
ba32b33796 | ||
|
|
6e77b017c5 | ||
|
|
5d058fa416 | ||
|
|
12c60b2f32 | ||
|
|
9801de6979 | ||
|
|
c695f2cd9c | ||
|
|
27c29aeb7b | ||
|
|
06f20603f6 | ||
|
|
607a9e3912 | ||
|
|
d3b4ad04f5 | ||
|
|
e7caf68edd | ||
|
|
c6374b6a1f | ||
|
|
8cde328007 | ||
|
|
02bad4b298 | ||
|
|
9b2179089b | ||
|
|
1c806b74ea | ||
|
|
8001955fb7 | ||
|
|
e6fa039f3f | ||
|
|
e0b784ac6d | ||
|
|
dc679081fa | ||
|
|
597c1f6667 | ||
|
|
125071cf00 | ||
|
|
2fb099e4cf | ||
|
|
6f9646a44f | ||
|
|
bd7b48c1b9 | ||
|
|
aac8132f47 | ||
|
|
7bdf3a39ad | ||
|
|
396543191c | ||
|
|
e24af3c78e | ||
|
|
e82147bbb8 | ||
|
|
d2c3cf49e1 | ||
|
|
e84d17196c | ||
|
|
fd4ae8304e | ||
|
|
83e2cf607f | ||
|
|
61b75639a7 | ||
|
|
19d266b528 | ||
|
|
9c4238c2f6 | ||
|
|
114ac72169 | ||
|
|
a10d7fbb02 | ||
|
|
f33af0b16a | ||
|
|
eeba7b3167 | ||
|
|
e5c30e6fae | ||
|
|
0b8218515e | ||
|
|
1a8a5ee000 | ||
|
|
1740fd036d | ||
|
|
6c635998f8 |
No files matched your search
@@ -0,0 +1,29 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
READ THIS FIRST!
|
||||
|
||||
If you have a usage question, please ask us on either Stack Overflow (https://stackoverflow.com/questions/tagged/jq) or in the #jq channel (http://irc.lc/freenode/%23jq/) on Freenode (https://webchat.freenode.net/).
|
||||
|
||||
-->
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Provide a minimal test case to reproduce the behavior.
|
||||
If the input is large, either attach it as a file, or [create a gist](https://gist.github.com) and link to it here.
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Environment (please complete the following information):**
|
||||
- OS and Version: [e.g. macOS, Windows, Linux (please specify distro)]
|
||||
- jq version [e.g. 1.5]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "modules/oniguruma"]
|
||||
path = modules/oniguruma
|
||||
url = https://github.com/kkos/oniguruma.git
|
||||
+183
-36
@@ -2,53 +2,200 @@ sudo: false
|
||||
|
||||
language: c
|
||||
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
stages:
|
||||
- name: test
|
||||
|
||||
matrix:
|
||||
- name: build
|
||||
# Don't run build stage for pull requests to save time and resources.
|
||||
if: type != pull_request
|
||||
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- compiler: gcc
|
||||
env: COVERAGE="--disable-valgrind --enable-gcov"
|
||||
# Build with gcc and run tests on Ubuntu.
|
||||
- &test-ubuntu
|
||||
stage: test
|
||||
os: linux
|
||||
compiler: gcc
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libonig-dev
|
||||
- valgrind
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- valgrind
|
||||
- bison
|
||||
- automake
|
||||
|
||||
before_install:
|
||||
- rm src/{lexer,parser}.{c,h}
|
||||
before_install:
|
||||
- uname -s
|
||||
- rvm install ruby-1.9.3-p551
|
||||
- rvm use 1.9.3
|
||||
- rm src/{lexer,parser}.{c,h}
|
||||
- sed -i.bak '/^AM_INIT_AUTOMAKE(\[-Wno-portability 1\.14\])$/s/14/11/' modules/oniguruma/configure.ac
|
||||
|
||||
install:
|
||||
- bundle install --gemfile=docs/Gemfile
|
||||
install:
|
||||
- bundle install --gemfile=docs/Gemfile
|
||||
- wget http://ftp.debian.org/debian/pool/main/b/bison/bison_3.0.2.dfsg-2_amd64.deb
|
||||
- ar p bison_3.0.2.dfsg-2_amd64.deb data.tar.xz | tar xJ
|
||||
- if [ -n "$COVERAGE" ]; then pip install --user cpp-coveralls; fi
|
||||
|
||||
- wget http://ftp.debian.org/debian/pool/main/b/bison/bison_3.0.2.dfsg-2_amd64.deb
|
||||
- ar p bison_3.0.2.dfsg-2_amd64.deb data.tar.xz | tar xJ
|
||||
before_script:
|
||||
# If this is OS X we'll get bison from brew, else we'll get bison
|
||||
# from the .deb unpacked above in the install section.
|
||||
- PATH=/usr/local/opt/bison/bin:$PWD/usr/bin:$PATH
|
||||
- echo SHELL=$SHELL
|
||||
- echo PATH=$PATH
|
||||
- which bison
|
||||
- bison --version
|
||||
- autoreconf -if
|
||||
- ./configure --with-oniguruma=builtin YACC="$(which bison) -y" $COVERAGE
|
||||
|
||||
- if [ -n "$COVERAGE" ]; then pip install --user cpp-coveralls; fi
|
||||
script:
|
||||
# When using the bison from Debian we need to tell that bison where
|
||||
# to find its data. Yay non-relocatable code. Not.
|
||||
- echo PATH=$PATH
|
||||
- which bison
|
||||
- make BISON_PKGDATADIR=$PWD/usr/share/bison src/parser.c || make src/parser.c
|
||||
# Make dist!
|
||||
#
|
||||
# Make it first to fail the build early, before we test with
|
||||
# valgrind.
|
||||
- make dist
|
||||
# Build and test the dist (without valgrind)
|
||||
- |
|
||||
(
|
||||
tar xvf jq-`scripts/version`.tar.gz &&
|
||||
cd jq-`scripts/version` &&
|
||||
pwd &&
|
||||
./configure --disable-valgrind --with-oniguruma=builtin YACC="$(which bison) -y" $COVERAGE &&
|
||||
make BISON_PKGDATADIR=$PWD/usr/share/bison src/parser.c || make src/parser.c &&
|
||||
make -j4 &&
|
||||
make check -j4 || true
|
||||
)
|
||||
# Build and test the HEAD
|
||||
- make -j4
|
||||
- make check -j4
|
||||
|
||||
before_script:
|
||||
- autoreconf -i
|
||||
- ./configure
|
||||
YACC="usr/bin/bison -y"
|
||||
$COVERAGE
|
||||
after_failure:
|
||||
- cat test-suite.log
|
||||
- cat tests/*.log
|
||||
|
||||
script:
|
||||
- make -j4 BISON_PKGDATADIR=$(pwd)/usr/share/bison
|
||||
- make check -j4
|
||||
|
||||
after_script:
|
||||
- |
|
||||
if [ -n "$COVERAGE" ]; then
|
||||
rm -rf src/.libs usr # don't care about coverage for libjq and bison
|
||||
coveralls --gcov-options '\-lp' \
|
||||
-e src/lexer.c -e src/parser.c -e src/jv_dtoa.c
|
||||
fi
|
||||
# Build with clang and run tests on Ubuntu.
|
||||
- <<: *test-ubuntu
|
||||
compiler: clang
|
||||
|
||||
|
||||
# Build with gcc and run tests with gcov on Ubuntu.
|
||||
- <<: *test-ubuntu
|
||||
env: COVERAGE="--disable-valgrind --enable-gcov"
|
||||
|
||||
after_script:
|
||||
- rm -rf src/.libs # don't care about coverage for libjq
|
||||
- coveralls --gcov-options '\-lp'
|
||||
-e src/lexer.c -e src/parser.c -e src/jv_dtoa.c
|
||||
|
||||
|
||||
# Build with gcc and run tests on macOS.
|
||||
- &test-osx
|
||||
<<: *test-ubuntu
|
||||
os: osx
|
||||
|
||||
before_install:
|
||||
- uname -s
|
||||
- brew update
|
||||
- brew install flex
|
||||
- brew install bison
|
||||
- rvm install ruby-1.9.3-p551
|
||||
- rvm use 1.9.3
|
||||
- rm src/{lexer,parser}.{c,h}
|
||||
- sed -i.bak '/^AM_INIT_AUTOMAKE(\[-Wno-portability 1\.14\])$/s/14/11/' modules/oniguruma/configure.ac
|
||||
|
||||
install:
|
||||
- bundle install --gemfile=docs/Gemfile
|
||||
- if [ -n "$COVERAGE" ]; then pip install --user cpp-coveralls; fi
|
||||
|
||||
|
||||
# Build with clang and run tests on macOS.
|
||||
- <<: *test-osx
|
||||
compiler: clang
|
||||
|
||||
|
||||
# Build with gcc and run tests on Alpine Linux v3.7 (inside chroot).
|
||||
# Note: Alpine uses musl libc.
|
||||
- &test-alpine
|
||||
stage: test
|
||||
os: linux
|
||||
language: minimal
|
||||
compiler: gcc
|
||||
sudo: true
|
||||
|
||||
before_install:
|
||||
- "wget 'https://raw.githubusercontent.com/alpinelinux/alpine-chroot-install/v0.7.0/alpine-chroot-install' \
|
||||
&& echo '090d323d887ef3a2fd4e752428553f22a52b87bb alpine-chroot-install' | sha1sum -c || travis_terminate 1"
|
||||
- alpine() { /alpine/enter-chroot -u "$USER" "$@"; }
|
||||
|
||||
install:
|
||||
- sudo sh alpine-chroot-install -b v3.7 -a "$ARCH"
|
||||
-p 'build-base automake autoconf bison libtool oniguruma-dev'
|
||||
|
||||
before_script:
|
||||
- autoreconf -if
|
||||
|
||||
script:
|
||||
- alpine ./configure --disable-docs
|
||||
- alpine make
|
||||
- alpine make check
|
||||
|
||||
|
||||
# Build release binary statically linked with musl libc on Alpine Linux
|
||||
# (inside chroot). If building a tagged commit, then deploy release
|
||||
# tarball to GitHub Releases.
|
||||
- &build-alpine
|
||||
<<: *test-alpine
|
||||
stage: build
|
||||
env: ARCH=x86_64
|
||||
|
||||
script:
|
||||
- alpine ./configure --disable-docs --enable-all-static
|
||||
CFLAGS='-Os -static -no-pie' CXXFLAGS='-Os -static -no-pie'
|
||||
- alpine make
|
||||
- alpine strip jq
|
||||
|
||||
- jq -V
|
||||
- ls -lah jq
|
||||
- file jq
|
||||
# Ensure that the built executable is really statically linked.
|
||||
- file jq | grep -Fw 'statically linked'
|
||||
|
||||
before_deploy:
|
||||
- PKGNAME="jq-$TRAVIS_TAG-$ARCH-linux"
|
||||
- mkdir $PKGNAME && mv jq $PKGNAME/
|
||||
- tar -czf $PKGNAME.tar.gz $PKGNAME/
|
||||
- sha256sum $PKGNAME.tar.gz > $PKGNAME.tar.gz.sha256
|
||||
|
||||
deploy:
|
||||
provider: releases
|
||||
api_key:
|
||||
secure: # TODO: put encrypted GitHub token here!
|
||||
file: jq-$TRAVIS_TAG-*.tar.gz*
|
||||
file_glob: true
|
||||
skip_cleanup: true
|
||||
on:
|
||||
tags: true
|
||||
|
||||
# Build binaries for other architectures using QEMU user-mode emulation.
|
||||
- <<: *build-alpine
|
||||
env: ARCH=x86
|
||||
|
||||
- <<: *build-alpine
|
||||
env: ARCH=aarch64
|
||||
|
||||
- <<: *build-alpine
|
||||
env: ARCH=armhf
|
||||
|
||||
- <<: *build-alpine
|
||||
env: ARCH=ppc64le
|
||||
|
||||
after_failure:
|
||||
- cat test-suite.log
|
||||
- cat tests/*.log
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
|
||||
@@ -4,7 +4,6 @@ Stephen Dolan <mu@netsoc.tcd.ie>
|
||||
Maintained by:
|
||||
Nicolas Williams <nico@cryptonector.com>
|
||||
William Langford <wlangfor@gmail.com>
|
||||
David Tolnay <dtolnay@gmail.com>
|
||||
|
||||
Contributions by:
|
||||
Aaron Peschel <apeschel@zendesk.com>
|
||||
@@ -20,11 +19,13 @@ cdnbacon <mike@busbud.com>
|
||||
Charles Merriam <charles.merriam@gmail.com>
|
||||
Colin von Heuring <colin@janrain.com>
|
||||
Damian Gryski <damian@gryski.com>
|
||||
David Fetter <david@fetter.org> - added --rawfile
|
||||
David Haguenauer <ml@kurokatta.org>
|
||||
David R. MacIver <david@drmaciver.com> - bug fixes
|
||||
David Tolnay <dtolnay@gmail.com> - destructuring, build improvements
|
||||
Doug Luce <doug@github.con.com> - build
|
||||
Eiichi Sato <sato.eiichi@gmail.com>
|
||||
Eric Bréchemier <eric@egull.co> - bug fix
|
||||
Filippo Giunchedi <fgiunchedi@gmail.com> - bug fixes
|
||||
Filippo Valsorda <filippo.valsorda@gmail.com> - recursive object merge (`*`)
|
||||
Hanfei Shen <qqshfox@gmail.com>
|
||||
@@ -59,6 +60,7 @@ Ryoichi KATO <ryo1kato@gmail.com> - doc fixes
|
||||
Rémy Léone <remy.leone@gmail.com> - add .travis.yml
|
||||
Santiago Lapresta <santiago.lapresta@gmail.com> - join, arrays, all, any, other filters
|
||||
Sebastian Freundt <freundt@ga-group.nl> - build
|
||||
Shaun Guth <shaun.guth@gmail.com> - base64d
|
||||
Shay Elkin <shay@everything.me>
|
||||
Simon Elsbrock <simon@iodev.org> - Debian
|
||||
Stefan Seemayer <stefan@seemayer.de>
|
||||
|
||||
@@ -6,8 +6,3 @@ pub 2048R/D15684DB 2015-10-12 [expires: 2017-10-11]
|
||||
Key fingerprint = 7F6C 7BD3 0412 AFD5 8C1A 5007 EB26 A4F8 D156 84DB
|
||||
uid Nicolas Williams <nico@cryptonector.com>
|
||||
sub 2048R/9C9CCD6A 2015-10-12 [expires: 2017-10-11]
|
||||
|
||||
pub 4096R/95FF6D82 2011-11-13
|
||||
Key fingerprint = 4628 C5D8 2CFF 65D6 8164 A78A F9BA 143B 95FF 6D82
|
||||
uid David Tolnay <dtolnay@gmail.com>
|
||||
sub 4096R/9572BD05 2011-11-13
|
||||
+23
-5
@@ -48,7 +48,7 @@ AM_YFLAGS = --warnings=all -d
|
||||
lib_LTLIBRARIES = libjq.la
|
||||
libjq_la_SOURCES = ${LIBJQ_SRC}
|
||||
libjq_la_LIBADD = -lm
|
||||
libjq_la_LDFLAGS = -export-symbols-regex '^j[qv]_' -version-info 1:4:0
|
||||
libjq_la_LDFLAGS = $(onig_LDFLAGS) -export-symbols-regex '^j[qv]_' -version-info 1:4:0
|
||||
|
||||
if WIN32
|
||||
libjq_la_LIBADD += -lshlwapi
|
||||
@@ -109,13 +109,17 @@ jq_SOURCES = src/main.c src/version.h
|
||||
jq_LDFLAGS = -static-libtool-libs
|
||||
jq_LDADD = libjq.la -lm
|
||||
|
||||
if WIN32
|
||||
jq_LDADD += -lshlwapi
|
||||
endif
|
||||
|
||||
if ENABLE_ALL_STATIC
|
||||
jq_LDFLAGS += -all-static
|
||||
endif
|
||||
|
||||
### Tests (make check)
|
||||
|
||||
TESTS = tests/optionaltest tests/mantest tests/jqtest tests/onigtest tests/shtest
|
||||
TESTS = tests/optionaltest tests/mantest tests/jqtest tests/onigtest tests/shtest tests/utf8test tests/base64test
|
||||
TESTS_ENVIRONMENT = NO_VALGRIND=$(NO_VALGRIND)
|
||||
|
||||
|
||||
@@ -133,6 +137,15 @@ jq.1: $(srcdir)/jq.1.prebuilt
|
||||
endif
|
||||
|
||||
|
||||
### Build oniguruma
|
||||
|
||||
if BUILD_ONIGURUMA
|
||||
libjq_la_LIBADD += modules/oniguruma/src/.libs/libonig.la
|
||||
SUBDIRS = modules/oniguruma
|
||||
endif
|
||||
|
||||
AM_CFLAGS += $(onig_CFLAGS)
|
||||
|
||||
### Packaging
|
||||
|
||||
docs/site.yml: configure.ac
|
||||
@@ -144,11 +157,11 @@ install-binaries: $(BUILT_SOURCES)
|
||||
|
||||
DOC_FILES = docs/content docs/public docs/templates docs/site.yml \
|
||||
docs/Gemfile docs/Gemfile.lock docs/Rakefile docs/README.md \
|
||||
docs/default_manpage.md jq.1.default
|
||||
jq.1.prebuilt
|
||||
|
||||
EXTRA_DIST = $(DOC_FILES) $(man_MANS) $(TESTS) $(TEST_LOG_COMPILER) \
|
||||
jq.1.prebuilt jq.spec src/lexer.c src/lexer.h src/parser.c \
|
||||
src/parser.h src/version.h src/builtin.inc \
|
||||
src/parser.h src/version.h src/builtin.jq \
|
||||
scripts/version tests/jq.test tests/modules/.jq \
|
||||
tests/modules/a.jq tests/modules/b/b.jq tests/modules/c/c.jq \
|
||||
tests/modules/c/d.jq tests/modules/data.json \
|
||||
@@ -158,7 +171,12 @@ EXTRA_DIST = $(DOC_FILES) $(man_MANS) $(TESTS) $(TEST_LOG_COMPILER) \
|
||||
tests/modules/test_bind_order0.jq \
|
||||
tests/modules/test_bind_order1.jq \
|
||||
tests/modules/test_bind_order2.jq tests/onig.supp \
|
||||
tests/onig.test tests/setup tests/torture/input0.json
|
||||
tests/onig.test tests/setup tests/torture/input0.json \
|
||||
tests/optional.test tests/optionaltest \
|
||||
tests/utf8-truncate.jq tests/utf8test \
|
||||
tests/base64.test tests/base64test \
|
||||
tests/jq-f-test.sh tests/shtest
|
||||
|
||||
|
||||
|
||||
# README.md is expected in Github projects, good stuff in it, so we'll
|
||||
|
||||
@@ -15,25 +15,29 @@ You can also try it online at [jqplay.org](https://jqplay.org).
|
||||
|
||||
If you want to hack on jq, feel free, but be warned that its internals
|
||||
are not well-documented at the moment. Bring a hard hat and a
|
||||
shovel. Also, read the wiki: https://github.com/stedolan/jq/wiki
|
||||
shovel. Also, read the wiki: https://github.com/stedolan/jq/wiki, where
|
||||
you will find cookbooks, discussion of advanced topics, internals,
|
||||
release engineering, and more.
|
||||
|
||||
Source tarball and built executable releases can be found on the
|
||||
homepage and on the github release page, https://github.com/stedolan/jq/releases
|
||||
|
||||
If you're building directly from the latest git, you'll need flex,
|
||||
bison (3.0 or newer), libtool, make, and autoconf installed. To get
|
||||
regexp support you'll also need to install Oniguruma (note that jq's
|
||||
tests require regexp support to pass). To build, run:
|
||||
bison (3.0 or newer), libtool, make, and autoconf installed.
|
||||
To get regexp support you'll also need to install Oniguruma or clone it as a
|
||||
git submodule as per the instructions below.
|
||||
(note that jq's tests require regexp support to pass). To build, run:
|
||||
|
||||
autoreconf -i # if building from git
|
||||
./configure
|
||||
git submodule update --init # if building from git to get oniguruma
|
||||
autoreconf -fi # if building from git
|
||||
./configure --with-oniguruma=builtin
|
||||
make -j8
|
||||
make check
|
||||
|
||||
To build without bison or flex, add `--disable-maintainer-mode` to the
|
||||
./configure invocation:
|
||||
|
||||
./configure --disable-maintainer-mode
|
||||
./configure --with-oniguruma=builtin --disable-maintainer-mode
|
||||
|
||||
(Developers must not use `--disable-maintainer-mode`, not when making
|
||||
changes to the jq parser and/or lexer.)
|
||||
|
||||
+10
-6
@@ -5,18 +5,21 @@ environment:
|
||||
- MSYSTEM: MINGW32
|
||||
PATH: C:\msys64\usr\bin;C:\msys64\mingw32\bin;C:\Windows\System32;C:\Windows;%PATH%
|
||||
|
||||
clone_script:
|
||||
- bash -lc "git clone -q --branch=$APPVEYOR_REPO_BRANCH https://github.com/${APPVEYOR_REPO_NAME}.git $APPVEYOR_BUILD_FOLDER"
|
||||
- bash -lc "cd $APPVEYOR_BUILD_FOLDER && git checkout -qf $APPVEYOR_REPO_BRANCH"
|
||||
- bash -lc "cd $APPVEYOR_BUILD_FOLDER && git submodule update --init --recursive"
|
||||
|
||||
install:
|
||||
# update mysy2
|
||||
- C:\msys64\usr\bin\bash -lc "pacman --needed --noconfirm -Sy pacman-mirrors"
|
||||
- C:\msys64\usr\bin\bash -lc "pacman --noconfirm -Sy"
|
||||
- C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S autoconf automake bison flex"
|
||||
- C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S mingw-w64-i686-oniguruma mingw-w64-x86_64-oniguruma"
|
||||
|
||||
- C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S git"
|
||||
|
||||
build_script:
|
||||
- bash -lc "exec 0</dev/null && cd $APPVEYOR_BUILD_FOLDER && autoreconf -i"
|
||||
- bash -lc "exec 0</dev/null && cd $APPVEYOR_BUILD_FOLDER && ./configure --disable-shared --enable-static --enable-all-static"
|
||||
- bash -lc "exec 0</dev/null && cd $APPVEYOR_BUILD_FOLDER && ./configure --with-oniguruma=builtin --disable-shared --enable-static --enable-all-static"
|
||||
- bash -lc "exec 0</dev/null && cd $APPVEYOR_BUILD_FOLDER && make -j8"
|
||||
- bash -lc "exec 0</dev/null && cd $APPVEYOR_BUILD_FOLDER && cat jq.1 | groff -mandoc -Thtml > jq.html"
|
||||
- 7z a jq-package.zip jq.1 jq.html jq.exe
|
||||
@@ -26,9 +29,9 @@ build_script:
|
||||
test_script:
|
||||
# tests/optionaltest and tests/shtest fail on Windows; run them
|
||||
# anyways but ignore their failures. Also, trace shtest.
|
||||
- bash -lc "exec 0</dev/null && cd $APPVEYOR_BUILD_FOLDER && make -j3 'TESTS=tests/mantest tests/jqtest tests/onigtest' check"
|
||||
- bash -lc "exec 0</dev/null && cd $APPVEYOR_BUILD_FOLDER && make TESTS=tests/optionaltest check || cat test-suite.log"
|
||||
- bash -lc "exec 0</dev/null && cd $APPVEYOR_BUILD_FOLDER && make TRACE_TESTS=1 TESTS=tests/shtest check || cat test-suite.log"
|
||||
- bash -lc "exec 0</dev/null && cd $APPVEYOR_BUILD_FOLDER && make -j3 SUBDIRS= 'TESTS=tests/mantest tests/jqtest tests/onigtest tests/base64test' check"
|
||||
- bash -lc "exec 0</dev/null && cd $APPVEYOR_BUILD_FOLDER && make SUBDIRS= TESTS=tests/optionaltest check || true"
|
||||
- bash -lc "exec 0</dev/null && cd $APPVEYOR_BUILD_FOLDER && make SUBDIRS= TRACE_TESTS=1 TESTS=tests/shtest check || true"
|
||||
|
||||
artifacts:
|
||||
- path: jq-package.zip
|
||||
@@ -42,6 +45,7 @@ artifacts:
|
||||
|
||||
|
||||
on_failure:
|
||||
- cat config.log
|
||||
- cat test-suite.log
|
||||
- appveyor PushArtifact test-suite.log
|
||||
# also push the jq.exe so that someone can download it anyways...
|
||||
|
||||
+4
-4
@@ -10,10 +10,10 @@ NJOBS="-j`sysctl -n hw.ncpu || echo 1`"
|
||||
|
||||
|
||||
# Get oniguruma
|
||||
rm -rf $PWD/build/ios onig-5.9.5
|
||||
echo "Downloading oniguruma 5.9.5"
|
||||
rm -rf $PWD/build/ios oniguruma-5.9.6
|
||||
echo "Downloading oniguruma 5.9.6"
|
||||
curl -L https://github.com/kkos/oniguruma/archive/v5.9.6.tar.gz | tar xz
|
||||
cd oniguruma-5.9.5
|
||||
cd oniguruma-5.9.6
|
||||
|
||||
# So, we need to remake the configure scripts so that the arm64 architecture
|
||||
# exists in config.sub. In order to keep autoreconf from failing, create
|
||||
@@ -50,7 +50,7 @@ for arch in i386 x86_64 armv7 armv7s arm64; do
|
||||
|
||||
|
||||
# Build oniguruma for this architecture
|
||||
cd onig-5.9.5
|
||||
cd oniguruma-5.9.6
|
||||
CC=$CC CFLAGS=$CFLAGS LDFLAGS=$LDFLAGS ./configure --disable-shared --enable-static --host=$HOST --prefix=$ORIG_PWD/build/ios/$arch
|
||||
STATUS=$?
|
||||
if [ $STATUS -ne 0 ]
|
||||
|
||||
+88
-38
@@ -1,4 +1,7 @@
|
||||
m4_define([jq_version], m4_esyscmd_s([git describe --tags --dirty --match 'jq-*'|sed 's/^jq-//']))
|
||||
m4_define([jq_version],
|
||||
m4_esyscmd_s([(git rev-parse --verify -q jq-1.0 > /dev/null &&
|
||||
(git describe --tags --dirty --match 'jq-*'|sed 's/^jq-//')) ||
|
||||
echo `git rev-parse --abbrev-ref HEAD`-`git describe --always --dirty`])))
|
||||
|
||||
AC_INIT([jq], [jq_version], [https://github.com/stedolan/jq/issues],
|
||||
[jq], [https://stedolan.github.io/jq])
|
||||
@@ -38,41 +41,9 @@ if test "$USE_MAINTAINER_MODE" = yes; then
|
||||
[AC_MSG_ERROR([You need bison version 3.0 or greater, or use --disable-maintainer-mode.])])
|
||||
fi
|
||||
|
||||
AC_PROG_LEX
|
||||
if test "x$LEX" != xflex; then
|
||||
LEX="$SHELL $missing_dir/missing flex"
|
||||
fi
|
||||
AC_CHECK_PROGS(LEX, flex lex)
|
||||
fi
|
||||
|
||||
|
||||
##########################################################################
|
||||
# check for ONIGURUMA library
|
||||
##########################################################################
|
||||
|
||||
AC_ARG_WITH([oniguruma],
|
||||
[AS_HELP_STRING([--with-oniguruma=prefix],
|
||||
[try this for a non-standard install prefix of the oniguruma library])],
|
||||
[],
|
||||
[with_oniguruma=yes])
|
||||
|
||||
AS_IF([test "x$with_oniguruma" != xno], [
|
||||
AS_IF([test "x$with_oniguruma" != xyes], [
|
||||
CFLAGS="$CFLAGS -I${with_oniguruma}/include"
|
||||
LDFLAGS="$LDFLAGS -L${with_oniguruma}/lib"
|
||||
])
|
||||
# check for ONIGURUMA library
|
||||
have_oniguruma=0
|
||||
AC_CHECK_HEADER("oniguruma.h",
|
||||
AC_CHECK_LIB([onig],[onig_version],[LIBS="$LIBS -lonig"; have_oniguruma=1;]))
|
||||
# handle check results
|
||||
AS_IF([test $have_oniguruma = 1], [
|
||||
AC_DEFINE([HAVE_ONIGURUMA], 1, [Define to 1 if Oniguruma is installed])
|
||||
], [
|
||||
AC_MSG_NOTICE([Oniguruma was not found.])
|
||||
AC_MSG_NOTICE([Try setting the location using '--with-oniguruma=PREFIX'])
|
||||
])
|
||||
])
|
||||
|
||||
dnl Check for valgrind
|
||||
AC_CHECK_PROGS(valgrind_cmd, valgrind)
|
||||
if test "x$valgrind_cmd" = "x" ; then
|
||||
@@ -156,6 +127,8 @@ AC_FIND_FUNC([strftime], [c], [#include <time.h>], [0, 0, 0, 0])
|
||||
AC_FIND_FUNC([timegm], [c], [#include <time.h>], [0])
|
||||
AC_FIND_FUNC([gmtime_r], [c], [#include <time.h>], [0, 0])
|
||||
AC_FIND_FUNC([gmtime], [c], [#include <time.h>], [0])
|
||||
AC_FIND_FUNC([localtime_r], [c], [#include <time.h>], [0, 0])
|
||||
AC_FIND_FUNC([localtime], [c], [#include <time.h>], [0])
|
||||
AC_FIND_FUNC([gettimeofday], [c], [#include <time.h>], [0, 0])
|
||||
AC_CHECK_MEMBER([struct tm.tm_gmtoff], [AC_DEFINE([HAVE_TM_TM_GMT_OFF],1,[Define to 1 if the system has the tm_gmt_off field in struct tm])],
|
||||
[], [[#include <time.h>]])
|
||||
@@ -179,31 +152,64 @@ AC_CHECK_MATH_FUNC(acos, [.5])
|
||||
AC_CHECK_MATH_FUNC(acosh, [.5])
|
||||
AC_CHECK_MATH_FUNC(asin, [.5])
|
||||
AC_CHECK_MATH_FUNC(asinh, [.5])
|
||||
AC_CHECK_MATH_FUNC(atan, [.5])
|
||||
AC_CHECK_MATH_FUNC(atan2, [.5,.5])
|
||||
AC_CHECK_MATH_FUNC(atan, [.5])
|
||||
AC_CHECK_MATH_FUNC(atanh, [.5])
|
||||
AC_CHECK_MATH_FUNC(cbrt, [.5])
|
||||
AC_CHECK_MATH_FUNC(ceil,[.5])
|
||||
AC_CHECK_MATH_FUNC(copysign,[.5,1.0])
|
||||
AC_CHECK_MATH_FUNC(cos, [.5])
|
||||
AC_CHECK_MATH_FUNC(cosh, [.5])
|
||||
AC_CHECK_MATH_FUNC(exp, [.5])
|
||||
AC_CHECK_MATH_FUNC(drem,[.5,1.0])
|
||||
AC_CHECK_MATH_FUNC(erf,[.5])
|
||||
AC_CHECK_MATH_FUNC(erfc,[.5])
|
||||
AC_CHECK_MATH_FUNC(exp10,[.5])
|
||||
AC_CHECK_MATH_FUNC(exp2, [.5])
|
||||
AC_CHECK_MATH_FUNC(exp, [.5])
|
||||
AC_CHECK_MATH_FUNC(expm1,[.5])
|
||||
AC_CHECK_MATH_FUNC(fabs,[.5])
|
||||
AC_CHECK_MATH_FUNC(fdim,[.5,1.0])
|
||||
AC_CHECK_MATH_FUNC(floor, [.5])
|
||||
AC_CHECK_MATH_FUNC(fma,[.5,1.0,1.5])
|
||||
AC_CHECK_MATH_FUNC(fmax,[.5,1.0])
|
||||
AC_CHECK_MATH_FUNC(fmin,[.5,1.0])
|
||||
AC_CHECK_MATH_FUNC(fmod,[.5,1.0])
|
||||
AC_FIND_FUNC([frexp], [m c], [#include <math.h>], [0, 0])
|
||||
AC_CHECK_MATH_FUNC(gamma,[.5])
|
||||
AC_CHECK_MATH_FUNC(hypot, [.5,.5])
|
||||
AC_CHECK_MATH_FUNC(j0, [.5])
|
||||
AC_CHECK_MATH_FUNC(j1, [.5])
|
||||
AC_CHECK_MATH_FUNC(log, [.5])
|
||||
AC_CHECK_MATH_FUNC(jn, [1,.5])
|
||||
AC_CHECK_MATH_FUNC(ldexp,[.5,2])
|
||||
AC_CHECK_MATH_FUNC(lgamma,[.5])
|
||||
AC_CHECK_MATH_FUNC(log10, [.5])
|
||||
AC_CHECK_MATH_FUNC(log1p,[.5])
|
||||
AC_CHECK_MATH_FUNC(log2, [.5])
|
||||
AC_CHECK_MATH_FUNC(log, [.5])
|
||||
AC_CHECK_MATH_FUNC(logb,[.5])
|
||||
AC_CHECK_MATH_FUNC([modf], [m c], [#include <math.h>], [0, 0])
|
||||
AC_CHECK_MATH_FUNC([lgamma_r], [m c], [#include <math.h>], [0, 0])
|
||||
AC_CHECK_MATH_FUNC(nearbyint,[.5])
|
||||
AC_CHECK_MATH_FUNC(nextafter,[.5,1.0])
|
||||
AC_CHECK_MATH_FUNC(nexttoward,[.5,1.0])
|
||||
AC_CHECK_MATH_FUNC(pow10,[.5])
|
||||
AC_CHECK_MATH_FUNC(pow, [2,2])
|
||||
AC_CHECK_MATH_FUNC(remainder, [3,2])
|
||||
AC_CHECK_MATH_FUNC(rint,[.5])
|
||||
AC_CHECK_MATH_FUNC(round,[.5])
|
||||
AC_CHECK_MATH_FUNC(scalb,[.5,1.0])
|
||||
AC_CHECK_MATH_FUNC(scalbln,[.5,2])
|
||||
AC_CHECK_MATH_FUNC(significand,[.5])
|
||||
AC_CHECK_MATH_FUNC(sin, [.5])
|
||||
AC_CHECK_MATH_FUNC(sinh, [.5])
|
||||
AC_CHECK_MATH_FUNC(sqrt, [.5])
|
||||
AC_CHECK_MATH_FUNC(tan, [.5])
|
||||
AC_CHECK_MATH_FUNC(tanh, [.5])
|
||||
AC_CHECK_MATH_FUNC(tgamma, [.5])
|
||||
AC_CHECK_MATH_FUNC(trunc,[.5])
|
||||
AC_CHECK_MATH_FUNC(y0, [.5])
|
||||
AC_CHECK_MATH_FUNC(y1, [.5])
|
||||
AC_CHECK_MATH_FUNC(yn, [1,.5])
|
||||
|
||||
dnl Thread local storage
|
||||
have___thread=no
|
||||
@@ -224,10 +230,54 @@ AC_C_BIGENDIAN(
|
||||
AC_MSG_ERROR(universial endianess not supported)
|
||||
)
|
||||
|
||||
dnl Oniguruma
|
||||
AC_ARG_WITH([oniguruma],
|
||||
[AS_HELP_STRING([--with-oniguruma=prefix],
|
||||
[try this for a non-standard install prefix of the oniguruma library])], ,
|
||||
[with_oniguruma=yes])
|
||||
|
||||
onig_CFLAGS=
|
||||
onig_LDFLAGS=
|
||||
build_oniguruma=no
|
||||
AS_IF([test "x$with_oniguruma" != xno], [
|
||||
save_CFLAGS="$CFLAGS"
|
||||
save_LDFLAGS="$LDFLAGS"
|
||||
AS_IF([test "x$with_oniguruma" != xyes], [
|
||||
AS_IF([test "x$with_oniguruma" = xbuiltin], [
|
||||
build_oniguruma=yes
|
||||
], [
|
||||
onig_CFLAGS="-I${with_oniguruma}/include"
|
||||
onig_LDFLAGS="-L${with_oniguruma}/lib"
|
||||
])
|
||||
])
|
||||
AS_IF([test "x$build_oniguruma" = xno], [
|
||||
# check for ONIGURUMA library, either in /usr or where requested
|
||||
CFLAGS="$CFLAGS $onig_CFLAGS"
|
||||
LDFLAGS="$LDFLAGS $onig_LDFLAGS"
|
||||
AC_CHECK_HEADER("oniguruma.h",
|
||||
AC_CHECK_LIB([onig],[onig_version]))
|
||||
# handle check results
|
||||
AS_IF([test "x$ac_cv_lib_onig_onig_version" != "xyes"], [
|
||||
build_oniguruma=yes
|
||||
AC_MSG_NOTICE([Oniguruma was not found. Will use the packaged oniguruma.])
|
||||
])
|
||||
])
|
||||
AS_IF([test "x$build_oniguruma" = xyes -a -f "${srcdir}/modules/oniguruma/configure.ac" ], [
|
||||
onig_CFLAGS="-I${srcdir}/modules/oniguruma/src"
|
||||
onig_LDFLAGS="-L${srcdir}/modules/oniguruma/src -Wl,-rpath,${libdir}"
|
||||
AC_CONFIG_SUBDIRS([modules/oniguruma])
|
||||
AC_DEFINE([HAVE_LIBONIG],1,[Define to 1 if the system includes libonig])
|
||||
])
|
||||
CFLAGS="$save_CFLAGS"
|
||||
LDFLAGS="$save_LDFLAGS"
|
||||
])
|
||||
AC_SUBST(onig_CFLAGS)
|
||||
AC_SUBST(onig_LDFLAGS)
|
||||
|
||||
AM_CONDITIONAL([BUILD_ONIGURUMA], [test "x$build_oniguruma" = xyes])
|
||||
AC_SUBST([BUNDLER], ["$bundle_cmd"])
|
||||
|
||||
AC_CONFIG_MACRO_DIR([config/m4])
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
AC_OUTPUT
|
||||
|
||||
|
||||
@@ -30,6 +30,16 @@ end
|
||||
|
||||
Liquid::Template.register_filter(ExtraFilters)
|
||||
|
||||
begin
|
||||
`java 2>&1`
|
||||
rescue
|
||||
class Bonsai::Exporter
|
||||
def self.compress_assets
|
||||
Bonsai.log "java not found! Not compressing javascript or stylesheets"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
task :build do
|
||||
Bonsai.root_dir = Dir.pwd
|
||||
Bonsai::Exporter.publish!
|
||||
|
||||
@@ -27,11 +27,9 @@ body:
|
||||
* jq 1.4 is in the official [openSUSE](https://software.opensuse.org/package/jq)
|
||||
repository. Install using `sudo zypper install jq`.
|
||||
|
||||
* For Arch users, a PKGBUILD is in the
|
||||
[AUR](https://aur.archlinux.org/packages/jq-git/).
|
||||
Refer to the
|
||||
[ArchWiki](https://wiki.archlinux.org/index.php/Arch_User_Repository)
|
||||
for how to install from AUR.
|
||||
* jq 1.5 is in the official
|
||||
[Arch](https://www.archlinux.org/packages/?sort=&q=jq&maintainer=&flagged=)
|
||||
repository. Install using `sudo pacman -Sy jq`.
|
||||
|
||||
* jq 1.5 binaries for
|
||||
[64-bit](https://github.com/stedolan/jq/releases/download/jq-1.5/jq-linux64)
|
||||
@@ -68,8 +66,12 @@ body:
|
||||
|
||||
### FreeBSD
|
||||
|
||||
* Use [FreshPorts](https://www.freshports.org/textproc/jq/) to install
|
||||
jq 1.4 with `pkg install jq`.
|
||||
* `pkg install jq` as root installs a pre-built
|
||||
[binary package](https://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/pkgng-intro.html).
|
||||
* `make -C /usr/ports/textproc/jq install clean` as root installs the
|
||||
[jq](https://www.freshports.org/textproc/jq/)
|
||||
[port](https://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/ports-using.html)
|
||||
from source.
|
||||
|
||||
### Solaris
|
||||
|
||||
|
||||
+476
-188
File diff suppressed because it is too large.
Load diff
@@ -336,7 +336,7 @@ sections:
|
||||
to the last element, -2 referring to the next to last element,
|
||||
and so on.
|
||||
|
||||
The `.foo` syntax only works for simply keys i.e. keys that
|
||||
The `.foo` syntax only works for simple keys i.e. keys that
|
||||
are all alphanumeric characters. `.[<string>]` works with
|
||||
keys that contain special characters such as colons and dots.
|
||||
For example `.["foo::bar"]` and `.["foo.bar"]` work while
|
||||
@@ -711,7 +711,7 @@ sections:
|
||||
- title: "`in`"
|
||||
body: |
|
||||
|
||||
The builtin function `in` returns the input key is in the
|
||||
The builtin function `in` returns whether or not the input key is in the
|
||||
given object, or the input index corresponds to an element
|
||||
in the given array. It is, essentially, an inversed version
|
||||
of `has`.
|
||||
@@ -728,8 +728,8 @@ sections:
|
||||
body: |
|
||||
|
||||
Outputs array representations of the given path expression
|
||||
in `.`. The outputs are arrays of strings (keys in objects0
|
||||
and/or numbers (array indices.
|
||||
in `.`. The outputs are arrays of strings (object keys)
|
||||
and/or numbers (array indices).
|
||||
|
||||
Path expressions are jq expressions like `.a`, but also `.[]`.
|
||||
There are two types of path expressions: ones that can match
|
||||
@@ -866,7 +866,7 @@ sections:
|
||||
body: |
|
||||
|
||||
For any filter `x`, `map(x)` will run that filter for each
|
||||
element of the input array, and produce the outputs a new
|
||||
element of the input array, and return the outputs in a new
|
||||
array. `map(.+1)` will increment each element of an array of numbers.
|
||||
|
||||
Similarly, `map_values(x)` will run that filter for each element,
|
||||
|
||||
+526
-142
File diff suppressed because it is too large.
Load diff
Submodule
+1
Submodule modules/oniguruma added at 6fa38f4084.
@@ -38,5 +38,6 @@ set -x
|
||||
for jq in `find . -type f \( -name jq -o -name jq.exe \) -print`; do
|
||||
cp "$jq" ..
|
||||
done
|
||||
cd ..
|
||||
rm -rf tmp
|
||||
|
||||
@@ -7,7 +7,6 @@ mask = lambda n: (1 << n) - 1
|
||||
|
||||
def print_table(type, name, t):
|
||||
print("static const %s %s[] =" % (type, name))
|
||||
first = True
|
||||
for i in range(0,len(t),16):
|
||||
print ((" {" if i == 0 else " ") +
|
||||
", ".join("0x%02x"%n for n in t[i:i+16]) +
|
||||
|
||||
+7
-2
@@ -1,5 +1,10 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
cd `dirname "$0"`
|
||||
git rev-parse --verify -q jq-1.0 > /dev/null 2>&1
|
||||
git describe --tags --match 'jq-*' --dirty | sed 's/^jq-//'
|
||||
if git rev-parse --verify -q jq-1.0 > /dev/null 2>&1; then
|
||||
git describe --tags --match 'jq-*' --dirty | sed 's/^jq-//'
|
||||
else
|
||||
b=`git rev-parse --abbrev-ref HEAD`
|
||||
c=`git describe --always --dirty`
|
||||
echo "${b}-${c}"
|
||||
fi
|
||||
+430
-63
@@ -1,7 +1,12 @@
|
||||
#define _BSD_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#define _XOPEN_SOURCE
|
||||
#define _XOPEN_SOURCE_EXTENDED 1
|
||||
#ifndef __sun__
|
||||
# define _XOPEN_SOURCE
|
||||
# define _XOPEN_SOURCE_EXTENDED 1
|
||||
#else
|
||||
# define _XPG6
|
||||
# define __EXTENSIONS__
|
||||
#endif
|
||||
#include <sys/time.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
@@ -24,7 +29,7 @@ void *alloca (size_t);
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#ifdef HAVE_ONIGURUMA
|
||||
#ifdef HAVE_LIBONIG
|
||||
#include <oniguruma.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
@@ -36,6 +41,7 @@ void *alloca (size_t);
|
||||
#include "linker.h"
|
||||
#include "locfile.h"
|
||||
#include "jv_unicode.h"
|
||||
#include "jv_alloc.h"
|
||||
|
||||
|
||||
static jv type_error(jv bad, const char* msg) {
|
||||
@@ -61,6 +67,17 @@ static jv type_error2(jv bad1, jv bad2, const char* msg) {
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline jv ret_error(jv bad, jv msg) {
|
||||
jv_free(bad);
|
||||
return jv_invalid_with_msg(msg);
|
||||
}
|
||||
|
||||
static inline jv ret_error2(jv bad1, jv bad2, jv msg) {
|
||||
jv_free(bad1);
|
||||
jv_free(bad2);
|
||||
return jv_invalid_with_msg(msg);
|
||||
}
|
||||
|
||||
static jv f_plus(jq_state *jq, jv input, jv a, jv b) {
|
||||
jv_free(input);
|
||||
if (jv_get_kind(a) == JV_KIND_NULL) {
|
||||
@@ -96,21 +113,90 @@ static jv f_ ## name(jq_state *jq, jv input) { \
|
||||
|
||||
#define LIBM_DDD(name) \
|
||||
static jv f_ ## name(jq_state *jq, jv input, jv a, jv b) { \
|
||||
if (jv_get_kind(a) != JV_KIND_NUMBER || jv_get_kind(b) != JV_KIND_NUMBER) \
|
||||
return type_error(input, "number required"); \
|
||||
jv_free(input); \
|
||||
if (jv_get_kind(a) != JV_KIND_NUMBER) { \
|
||||
jv_free(b); \
|
||||
return type_error(a, "number required"); \
|
||||
} \
|
||||
if (jv_get_kind(b) != JV_KIND_NUMBER) { \
|
||||
jv_free(a); \
|
||||
return type_error(b, "number required"); \
|
||||
} \
|
||||
jv ret = jv_number(name(jv_number_value(a), jv_number_value(b))); \
|
||||
jv_free(a); \
|
||||
jv_free(b); \
|
||||
return ret; \
|
||||
}
|
||||
#define LIBM_DDD_NO(name)
|
||||
|
||||
#define LIBM_DDDD(name) \
|
||||
static jv f_ ## name(jq_state *jq, jv input, jv a, jv b, jv c) { \
|
||||
jv_free(input); \
|
||||
if (jv_get_kind(a) != JV_KIND_NUMBER) { \
|
||||
jv_free(b); \
|
||||
jv_free(c); \
|
||||
return type_error(a, "number required"); \
|
||||
} \
|
||||
if (jv_get_kind(b) != JV_KIND_NUMBER) { \
|
||||
jv_free(a); \
|
||||
jv_free(c); \
|
||||
return type_error(b, "number required"); \
|
||||
} \
|
||||
if (jv_get_kind(c) != JV_KIND_NUMBER) { \
|
||||
jv_free(a); \
|
||||
jv_free(b); \
|
||||
return type_error(c, "number required"); \
|
||||
} \
|
||||
jv ret = jv_number(name(jv_number_value(a), jv_number_value(b), jv_number_value(c))); \
|
||||
jv_free(a); \
|
||||
jv_free(b); \
|
||||
jv_free(c); \
|
||||
return ret; \
|
||||
}
|
||||
#define LIBM_DDDD_NO(name)
|
||||
#include "libm.h"
|
||||
#undef LIBM_DDDD_NO
|
||||
#undef LIBM_DDD_NO
|
||||
#undef LIBM_DD_NO
|
||||
#undef LIBM_DDDD
|
||||
#undef LIBM_DDD
|
||||
#undef LIBM_DD
|
||||
|
||||
#ifdef HAVE_FREXP
|
||||
static jv f_frexp(jq_state *jq, jv input) {
|
||||
if (jv_get_kind(input) != JV_KIND_NUMBER) {
|
||||
return type_error(input, "number required");
|
||||
}
|
||||
int exp;
|
||||
double d = frexp(jv_number_value(input), &exp);
|
||||
jv ret = JV_ARRAY(jv_number(d), jv_number(exp));
|
||||
jv_free(input);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_MODF
|
||||
static jv f_modf(jq_state *jq, jv input) {
|
||||
if (jv_get_kind(input) != JV_KIND_NUMBER) {
|
||||
return type_error(input, "number required");
|
||||
}
|
||||
double i;
|
||||
jv ret = JV_ARRAY(jv_number(modf(jv_number_value(input), &i)));
|
||||
jv_free(input);
|
||||
return jv_array_append(ret, jv_number(i));
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_LGAMMA_R
|
||||
static jv f_lgamma_r(jq_state *jq, jv input) {
|
||||
if (jv_get_kind(input) != JV_KIND_NUMBER) {
|
||||
return type_error(input, "number required");
|
||||
}
|
||||
int sign;
|
||||
jv ret = JV_ARRAY(jv_number(lgamma_r(jv_number_value(input), &sign)));
|
||||
jv_free(input);
|
||||
return jv_array_append(ret, jv_number(sign));
|
||||
}
|
||||
#endif
|
||||
|
||||
static jv f_negate(jq_state *jq, jv input) {
|
||||
if (jv_get_kind(input) != JV_KIND_NUMBER) {
|
||||
return type_error(input, "cannot be negated");
|
||||
@@ -122,7 +208,7 @@ static jv f_negate(jq_state *jq, jv input) {
|
||||
|
||||
static jv f_startswith(jq_state *jq, jv a, jv b) {
|
||||
if (jv_get_kind(a) != JV_KIND_STRING || jv_get_kind(b) != JV_KIND_STRING)
|
||||
return jv_invalid_with_msg(jv_string("startswith() requires string inputs"));
|
||||
return ret_error2(a, b, jv_string("startswith() requires string inputs"));
|
||||
int alen = jv_string_length_bytes(jv_copy(a));
|
||||
int blen = jv_string_length_bytes(jv_copy(b));
|
||||
jv ret;
|
||||
@@ -138,7 +224,7 @@ static jv f_startswith(jq_state *jq, jv a, jv b) {
|
||||
|
||||
static jv f_endswith(jq_state *jq, jv a, jv b) {
|
||||
if (jv_get_kind(a) != JV_KIND_STRING || jv_get_kind(b) != JV_KIND_STRING)
|
||||
return jv_invalid_with_msg(jv_string("endswith() requires string inputs"));
|
||||
return ret_error2(a, b, jv_string("endswith() requires string inputs"));
|
||||
const char *astr = jv_string_value(a);
|
||||
const char *bstr = jv_string_value(b);
|
||||
size_t alen = jv_string_length_bytes(jv_copy(a));
|
||||
@@ -376,6 +462,24 @@ static jv f_utf8bytelength(jq_state *jq, jv input) {
|
||||
|
||||
#define CHARS_ALPHANUM "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
|
||||
static const unsigned char BASE64_ENCODE_TABLE[64 + 1] = CHARS_ALPHANUM "+/";
|
||||
static const unsigned char BASE64_INVALID_ENTRY = 0xFF;
|
||||
static const unsigned char BASE64_DECODE_TABLE[255] = {
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
62, // +
|
||||
0xFF, 0xFF, 0xFF,
|
||||
63, // /
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // 0-9
|
||||
0xFF, 0xFF, 0xFF,
|
||||
99, // =
|
||||
0xFF, 0xFF, 0xFF,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // A-Z
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // a-z
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
|
||||
static jv escape_string(jv input, const char* escapings) {
|
||||
|
||||
assert(jv_get_kind(input) == JV_KIND_STRING);
|
||||
@@ -528,7 +632,6 @@ static jv f_format(jq_state *jq, jv input, jv fmt) {
|
||||
jv_free(fmt);
|
||||
input = f_tostring(jq, input);
|
||||
jv line = jv_string("");
|
||||
const char b64[64 + 1] = CHARS_ALPHANUM "+/";
|
||||
const unsigned char* data = (const unsigned char*)jv_string_value(input);
|
||||
int len = jv_string_length_bytes(jv_copy(input));
|
||||
for (int i=0; i<len; i+=3) {
|
||||
@@ -540,7 +643,7 @@ static jv f_format(jq_state *jq, jv input, jv fmt) {
|
||||
}
|
||||
char buf[4];
|
||||
for (int j=0; j<4; j++) {
|
||||
buf[j] = b64[(code >> (18 - j*6)) & 0x3f];
|
||||
buf[j] = BASE64_ENCODE_TABLE[(code >> (18 - j*6)) & 0x3f];
|
||||
}
|
||||
if (n < 3) buf[3] = '=';
|
||||
if (n < 2) buf[2] = '=';
|
||||
@@ -548,6 +651,49 @@ static jv f_format(jq_state *jq, jv input, jv fmt) {
|
||||
}
|
||||
jv_free(input);
|
||||
return line;
|
||||
} else if (!strcmp(fmt_s, "base64d")) {
|
||||
jv_free(fmt);
|
||||
input = f_tostring(jq, input);
|
||||
const unsigned char* data = (const unsigned char*)jv_string_value(input);
|
||||
int len = jv_string_length_bytes(jv_copy(input));
|
||||
size_t decoded_len = (3 * len) / 4; // 3 usable bytes for every 4 bytes of input
|
||||
char *result = jv_mem_calloc(decoded_len, sizeof(char));
|
||||
memset(result, 0, decoded_len * sizeof(char));
|
||||
uint32_t ri = 0;
|
||||
int input_bytes_read=0;
|
||||
uint32_t code = 0;
|
||||
for (int i=0; i<len && data[i] != '='; i++) {
|
||||
if (BASE64_DECODE_TABLE[data[i]] == BASE64_INVALID_ENTRY) {
|
||||
free(result);
|
||||
return type_error(input, "is not valid base64 data");
|
||||
}
|
||||
|
||||
code <<= 6;
|
||||
code |= BASE64_DECODE_TABLE[data[i]];
|
||||
input_bytes_read++;
|
||||
|
||||
if (input_bytes_read == 4) {
|
||||
result[ri++] = (code >> 16) & 0xFF;
|
||||
result[ri++] = (code >> 8) & 0xFF;
|
||||
result[ri++] = code & 0xFF;
|
||||
input_bytes_read = 0;
|
||||
code = 0;
|
||||
}
|
||||
}
|
||||
if (input_bytes_read == 3) {
|
||||
result[ri++] = (code >> 10) & 0xFF;
|
||||
result[ri++] = (code >> 2) & 0xFF;
|
||||
} else if (input_bytes_read == 2) {
|
||||
result[ri++] = (code >> 4) & 0xFF;
|
||||
} else if (input_bytes_read == 1) {
|
||||
free(result);
|
||||
return type_error(input, "trailing base64 byte found");
|
||||
}
|
||||
|
||||
jv line = jv_string_sized(result, ri);
|
||||
jv_free(input);
|
||||
free(result);
|
||||
return line;
|
||||
} else {
|
||||
jv_free(input);
|
||||
return jv_invalid_with_msg(jv_string_concat(fmt, jv_string(" is not a valid format")));
|
||||
@@ -598,7 +744,7 @@ static jv f_group_by_impl(jq_state *jq, jv input, jv keys) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_ONIGURUMA
|
||||
#ifdef HAVE_LIBONIG
|
||||
static int f_match_name_iter(const UChar* name, const UChar *name_end, int ngroups,
|
||||
int *groups, regex_t *reg, void *arg) {
|
||||
jv captures = *(jv*)arg;
|
||||
@@ -802,11 +948,11 @@ static jv f_match(jq_state *jq, jv input, jv regex, jv modifiers, jv testmode) {
|
||||
jv_free(regex);
|
||||
return result;
|
||||
}
|
||||
#else /* ! HAVE_ONIGURUMA */
|
||||
#else /* !HAVE_LIBONIG */
|
||||
static jv f_match(jq_state *jq, jv input, jv regex, jv modifiers, jv testmode) {
|
||||
return jv_invalid_with_msg(jv_string("jq was compiled without ONIGURUMA regex libary. match/test/sub and related functions are not available."));
|
||||
}
|
||||
#endif /* HAVE_ONIGURUMA */
|
||||
#endif /* HAVE_LIBONIG */
|
||||
|
||||
static jv minmax_by(jv values, jv keys, int is_min) {
|
||||
if (jv_get_kind(values) != JV_KIND_ARRAY)
|
||||
@@ -914,14 +1060,14 @@ static jv f_error(jq_state *jq, jv input, jv msg) {
|
||||
|
||||
// FIXME Should autoconf check for this!
|
||||
#ifndef WIN32
|
||||
extern const char **environ;
|
||||
extern char **environ;
|
||||
#endif
|
||||
|
||||
static jv f_env(jq_state *jq, jv input) {
|
||||
jv_free(input);
|
||||
jv env = jv_object();
|
||||
const char *var, *val;
|
||||
for (const char **e = environ; *e != NULL; e++) {
|
||||
for (char **e = environ; *e != NULL; e++) {
|
||||
var = e[0];
|
||||
val = strchr(e[0], '=');
|
||||
if (val == NULL)
|
||||
@@ -932,6 +1078,21 @@ static jv f_env(jq_state *jq, jv input) {
|
||||
return env;
|
||||
}
|
||||
|
||||
static jv f_halt(jq_state *jq, jv input) {
|
||||
jv_free(input);
|
||||
jq_halt(jq, jv_invalid(), jv_invalid());
|
||||
return jv_true();
|
||||
}
|
||||
|
||||
static jv f_halt_error(jq_state *jq, jv input, jv a) {
|
||||
if (jv_get_kind(a) != JV_KIND_NUMBER) {
|
||||
jv_free(a);
|
||||
return type_error(input, "halt_error/1: number required");
|
||||
}
|
||||
jq_halt(jq, a, input);
|
||||
return jv_true();
|
||||
}
|
||||
|
||||
static jv f_get_search_list(jq_state *jq, jv input) {
|
||||
jv_free(input);
|
||||
return jq_get_lib_dirs(jq);
|
||||
@@ -949,17 +1110,14 @@ static jv f_get_jq_origin(jq_state *jq, jv input) {
|
||||
|
||||
static jv f_string_split(jq_state *jq, jv a, jv b) {
|
||||
if (jv_get_kind(a) != JV_KIND_STRING || jv_get_kind(b) != JV_KIND_STRING) {
|
||||
jv_free(a);
|
||||
jv_free(b);
|
||||
return jv_invalid_with_msg(jv_string("split input and separator must be strings"));
|
||||
return ret_error2(a, b, jv_string("split input and separator must be strings"));
|
||||
}
|
||||
return jv_string_split(a, b);
|
||||
}
|
||||
|
||||
static jv f_string_explode(jq_state *jq, jv a) {
|
||||
if (jv_get_kind(a) != JV_KIND_STRING) {
|
||||
jv_free(a);
|
||||
return jv_invalid_with_msg(jv_string("explode input must be a string"));
|
||||
return ret_error(a, jv_string("explode input must be a string"));
|
||||
}
|
||||
return jv_string_explode(a);
|
||||
}
|
||||
@@ -970,21 +1128,22 @@ static jv f_string_indexes(jq_state *jq, jv a, jv b) {
|
||||
|
||||
static jv f_string_implode(jq_state *jq, jv a) {
|
||||
if (jv_get_kind(a) != JV_KIND_ARRAY) {
|
||||
jv_free(a);
|
||||
return jv_invalid_with_msg(jv_string("implode input must be an array"));
|
||||
return ret_error(a, jv_string("implode input must be an array"));
|
||||
}
|
||||
return jv_string_implode(a);
|
||||
}
|
||||
|
||||
static jv f_setpath(jq_state *jq, jv a, jv b, jv c) { return jv_setpath(a, b, c); }
|
||||
static jv f_getpath(jq_state *jq, jv a, jv b) { return jv_getpath(a, b); }
|
||||
extern jv _jq_path_append(jq_state *, jv, jv, jv);
|
||||
static jv f_getpath(jq_state *jq, jv a, jv b) {
|
||||
return _jq_path_append(jq, a, b, jv_getpath(jv_copy(a), jv_copy(b)));
|
||||
}
|
||||
static jv f_delpaths(jq_state *jq, jv a, jv b) { return jv_delpaths(a, b); }
|
||||
static jv f_has(jq_state *jq, jv a, jv b) { return jv_has(a, b); }
|
||||
|
||||
static jv f_modulemeta(jq_state *jq, jv a) {
|
||||
if (jv_get_kind(a) != JV_KIND_STRING) {
|
||||
jv_free(a);
|
||||
return jv_invalid_with_msg(jv_string("modulemeta input module name must be a string"));
|
||||
return ret_error(a, jv_string("modulemeta input module name must be a string"));
|
||||
}
|
||||
return load_module_meta(jq, a);
|
||||
}
|
||||
@@ -1013,7 +1172,6 @@ static jv f_debug(jq_state *jq, jv input) {
|
||||
|
||||
static jv f_stderr(jq_state *jq, jv input) {
|
||||
jv_dumpf(jv_copy(input), stderr, 0);
|
||||
fprintf(stderr, "\n");
|
||||
return input;
|
||||
}
|
||||
|
||||
@@ -1043,45 +1201,135 @@ static jv tm2jv(struct tm *tm) {
|
||||
*
|
||||
* Returns (time_t)-2 if mktime()'s side-effects cannot be corrected.
|
||||
*/
|
||||
static time_t my_mktime(struct tm *tm) {
|
||||
static time_t my_timegm(struct tm *tm) {
|
||||
#ifdef HAVE_TIMEGM
|
||||
return timegm(tm);
|
||||
#else /* HAVE_TIMEGM */
|
||||
char *tz;
|
||||
|
||||
tz = (tz = getenv("TZ")) != NULL ? strdup(tz) : NULL;
|
||||
if (tz != NULL)
|
||||
setenv("TZ", "", 1);
|
||||
time_t t = mktime(tm);
|
||||
if (tz != NULL)
|
||||
setenv("TZ", tz, 1);
|
||||
return t;
|
||||
#endif /* !HAVE_TIMEGM */
|
||||
}
|
||||
static time_t my_mktime(struct tm *tm) {
|
||||
time_t t = mktime(tm);
|
||||
if (t == (time_t)-1)
|
||||
return t;
|
||||
#ifdef HAVE_TM_TM_GMT_OFF
|
||||
return t + tm.tm_gmtoff;
|
||||
#elif defined(HAVE_TM_TM_GMT_OFF)
|
||||
return t + tm.__tm_gmtoff;
|
||||
return t + tm->tm_gmtoff;
|
||||
#elif HAVE_TM___TM_GMT_OFF
|
||||
return t + tm->__tm_gmtoff;
|
||||
#else
|
||||
return (time_t)-2; /* Not supported */
|
||||
#endif
|
||||
#endif /* !HAVE_TIMEGM */
|
||||
}
|
||||
|
||||
/* Compute and set tm_wday */
|
||||
static void set_tm_wday(struct tm *tm) {
|
||||
/*
|
||||
* https://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week#Gauss.27s_algorithm
|
||||
* https://cs.uwaterloo.ca/~alopez-o/math-faq/node73.html
|
||||
*
|
||||
* Tested with dates from 1900-01-01 through 2100-01-01. This
|
||||
* algorithm produces the wrong day-of-the-week number for dates in
|
||||
* the range 1900-01-01..1900-02-28, and for 2100-01-01..2100-02-28.
|
||||
* Since this is only needed on OS X and *BSD, we might just document
|
||||
* this.
|
||||
*/
|
||||
int century = (1900 + tm->tm_year) / 100;
|
||||
int year = (1900 + tm->tm_year) % 100;
|
||||
if (tm->tm_mon < 2)
|
||||
year--;
|
||||
/*
|
||||
* The month value in the wday computation below is shifted so that
|
||||
* March is 1, April is 2, .., January is 11, and February is 12.
|
||||
*/
|
||||
int mon = tm->tm_mon - 1;
|
||||
if (mon < 1)
|
||||
mon += 12;
|
||||
int wday =
|
||||
(tm->tm_mday + (int)floor((2.6 * mon - 0.2)) + year + (int)floor(year / 4.0) + (int)floor(century / 4.0) - 2 * century) % 7;
|
||||
if (wday < 0)
|
||||
wday += 7;
|
||||
#if 0
|
||||
/* See commentary above */
|
||||
assert(wday == tm->tm_wday || tm->tm_wday == 8);
|
||||
#endif
|
||||
tm->tm_wday = wday;
|
||||
}
|
||||
/*
|
||||
* Compute and set tm_yday.
|
||||
*
|
||||
*/
|
||||
static void set_tm_yday(struct tm *tm) {
|
||||
static const int d[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
|
||||
int mon = tm->tm_mon;
|
||||
int year = 1900 + tm->tm_year;
|
||||
int leap_day = 0;
|
||||
if (tm->tm_mon > 1 &&
|
||||
((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
|
||||
leap_day = 1;
|
||||
|
||||
/* Bound check index into d[] */
|
||||
if (mon < 0)
|
||||
mon = -mon;
|
||||
if (mon > 11)
|
||||
mon %= 12;
|
||||
|
||||
int yday = d[mon] + leap_day + tm->tm_mday - 1;
|
||||
assert(yday == tm->tm_yday || tm->tm_yday == 367);
|
||||
tm->tm_yday = yday;
|
||||
}
|
||||
|
||||
#ifdef HAVE_STRPTIME
|
||||
static jv f_strptime(jq_state *jq, jv a, jv b) {
|
||||
if (jv_get_kind(a) != JV_KIND_STRING || jv_get_kind(b) != JV_KIND_STRING)
|
||||
return jv_invalid_with_msg(jv_string("strptime/1 requires string inputs and arguments"));
|
||||
if (jv_get_kind(a) != JV_KIND_STRING || jv_get_kind(b) != JV_KIND_STRING) {
|
||||
return ret_error2(a, b, jv_string("strptime/1 requires string inputs and arguments"));
|
||||
}
|
||||
|
||||
struct tm tm;
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
tm.tm_wday = 8; // sentinel
|
||||
tm.tm_yday = 367; // sentinel
|
||||
const char *input = jv_string_value(a);
|
||||
const char *fmt = jv_string_value(b);
|
||||
const char *end = strptime(input, fmt, &tm);
|
||||
|
||||
if (end == NULL || (*end != '\0' && !isspace(*end))) {
|
||||
jv e = jv_invalid_with_msg(jv_string_fmt("date \"%s\" does not match format \"%s\"", input, fmt));
|
||||
jv_free(a);
|
||||
jv_free(b);
|
||||
return e;
|
||||
return ret_error2(a, b, jv_string_fmt("date \"%s\" does not match format \"%s\"", input, fmt));
|
||||
}
|
||||
jv_free(b);
|
||||
if (tm.tm_wday == 0 && tm.tm_yday == 0 && my_mktime(&tm) == (time_t)-2) {
|
||||
jv_free(a);
|
||||
return jv_invalid_with_msg(jv_string("strptime/1 not supported on this platform"));
|
||||
}
|
||||
/*
|
||||
* This is OS X or some *BSD whose strptime() is just not that
|
||||
* helpful!
|
||||
*
|
||||
* We don't know that the format string did involve parsing a
|
||||
* year, or a month (if tm->tm_mon == 0). But with our invalid
|
||||
* day-of-week and day-of-year sentinel checks above, the worst
|
||||
* this can do is produce garbage.
|
||||
*/
|
||||
#ifdef __APPLE__
|
||||
/*
|
||||
* Apple has made it worse, and different versions of the OS have different
|
||||
* behaviors. Some versions just don't touch the fields, but others do, and
|
||||
* sometimes provide wrong answers, at that! We can't tell at compile-time
|
||||
* which behavior the target system will have, so instead we always use our
|
||||
* functions to set these on OS X, and document that %u and %j are
|
||||
* unsupported on OS X.
|
||||
*/
|
||||
set_tm_wday(&tm);
|
||||
set_tm_yday(&tm);
|
||||
#else
|
||||
if (tm.tm_wday == 8 && tm.tm_mday != 0 && tm.tm_mon >= 0 && tm.tm_mon <= 11)
|
||||
set_tm_wday(&tm);
|
||||
if (tm.tm_yday == 367 && tm.tm_mday != 0 && tm.tm_mon >= 0 && tm.tm_mon <= 11)
|
||||
set_tm_yday(&tm);
|
||||
#endif
|
||||
jv r = tm2jv(&tm);
|
||||
if (*end != '\0')
|
||||
r = jv_array_append(r, jv_string(end));
|
||||
@@ -1099,8 +1347,10 @@ static jv f_strptime(jq_state *jq, jv a, jv b) {
|
||||
#define TO_TM_FIELD(t, j, i) \
|
||||
do { \
|
||||
jv n = jv_array_get(jv_copy(j), (i)); \
|
||||
if (jv_get_kind(n) != (JV_KIND_NUMBER)) \
|
||||
if (jv_get_kind(n) != (JV_KIND_NUMBER)) { \
|
||||
jv_free(j); \
|
||||
return 0; \
|
||||
} \
|
||||
t = jv_number_value(n); \
|
||||
jv_free(n); \
|
||||
} while (0)
|
||||
@@ -1133,9 +1383,9 @@ static int jv2tm(jv a, struct tm *tm) {
|
||||
|
||||
static jv f_mktime(jq_state *jq, jv a) {
|
||||
if (jv_get_kind(a) != JV_KIND_ARRAY)
|
||||
return jv_invalid_with_msg(jv_string("mktime requires array inputs"));
|
||||
return ret_error(a, jv_string("mktime requires array inputs"));
|
||||
if (jv_array_length(jv_copy(a)) < 6)
|
||||
return jv_invalid_with_msg(jv_string("mktime requires parsed datetime inputs"));
|
||||
return ret_error(a, jv_string("mktime requires parsed datetime inputs"));
|
||||
struct tm tm;
|
||||
if (!jv2tm(a, &tm))
|
||||
return jv_invalid_with_msg(jv_string("mktime requires parsed datetime inputs"));
|
||||
@@ -1150,7 +1400,7 @@ static jv f_mktime(jq_state *jq, jv a) {
|
||||
#ifdef HAVE_GMTIME_R
|
||||
static jv f_gmtime(jq_state *jq, jv a) {
|
||||
if (jv_get_kind(a) != JV_KIND_NUMBER)
|
||||
return jv_invalid_with_msg(jv_string("gmtime() requires numeric inputs"));
|
||||
return ret_error(a, jv_string("gmtime() requires numeric inputs"));
|
||||
struct tm tm, *tmp;
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
double fsecs = jv_number_value(a);
|
||||
@@ -1165,7 +1415,7 @@ static jv f_gmtime(jq_state *jq, jv a) {
|
||||
#elif defined HAVE_GMTIME
|
||||
static jv f_gmtime(jq_state *jq, jv a) {
|
||||
if (jv_get_kind(a) != JV_KIND_NUMBER)
|
||||
return jv_invalid_with_msg(jv_string("gmtime requires numeric inputs"));
|
||||
return ret_error(a, jv_string("gmtime requires numeric inputs"));
|
||||
struct tm tm, *tmp;
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
double fsecs = jv_number_value(a);
|
||||
@@ -1184,16 +1434,56 @@ static jv f_gmtime(jq_state *jq, jv a) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LOCALTIME_R
|
||||
static jv f_localtime(jq_state *jq, jv a) {
|
||||
if (jv_get_kind(a) != JV_KIND_NUMBER)
|
||||
return ret_error(a, jv_string("localtime() requires numeric inputs"));
|
||||
struct tm tm, *tmp;
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
double fsecs = jv_number_value(a);
|
||||
time_t secs = fsecs;
|
||||
jv_free(a);
|
||||
tmp = localtime_r(&secs, &tm);
|
||||
if (tmp == NULL)
|
||||
return jv_invalid_with_msg(jv_string("error converting number of seconds since epoch to datetime"));
|
||||
a = tm2jv(tmp);
|
||||
return jv_array_set(a, 5, jv_number(jv_number_value(jv_array_get(jv_copy(a), 5)) + (fsecs - floor(fsecs))));
|
||||
}
|
||||
#elif defined HAVE_GMTIME
|
||||
static jv f_localtime(jq_state *jq, jv a) {
|
||||
if (jv_get_kind(a) != JV_KIND_NUMBER)
|
||||
return ret_error(a, jv_string("localtime requires numeric inputs"));
|
||||
struct tm tm, *tmp;
|
||||
memset(&tm, 0, sizeof(tm));
|
||||
double fsecs = jv_number_value(a);
|
||||
time_t secs = fsecs;
|
||||
jv_free(a);
|
||||
tmp = localtime(&secs);
|
||||
if (tmp == NULL)
|
||||
return jv_invalid_with_msg(jv_string("error converting number of seconds since epoch to datetime"));
|
||||
a = tm2jv(tmp);
|
||||
return jv_array_set(a, 5, jv_number(jv_number_value(jv_array_get(jv_copy(a), 5)) + (fsecs - floor(fsecs))));
|
||||
}
|
||||
#else
|
||||
static jv f_localtime(jq_state *jq, jv a) {
|
||||
jv_free(a);
|
||||
return jv_invalid_with_msg(jv_string("localtime not implemented on this platform"));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STRFTIME
|
||||
static jv f_strftime(jq_state *jq, jv a, jv b) {
|
||||
if (jv_get_kind(a) == JV_KIND_NUMBER) {
|
||||
a = f_gmtime(jq, a);
|
||||
} else if (jv_get_kind(a) != JV_KIND_ARRAY) {
|
||||
return jv_invalid_with_msg(jv_string("strftime/1 requires parsed datetime inputs"));
|
||||
return ret_error2(a, b, jv_string("strftime/1 requires parsed datetime inputs"));
|
||||
} else if (jv_get_kind(b) != JV_KIND_STRING) {
|
||||
return ret_error2(a, b, jv_string("strftime/1 requires a string format"));
|
||||
}
|
||||
struct tm tm;
|
||||
if (!jv2tm(a, &tm))
|
||||
return jv_invalid_with_msg(jv_string("strftime/1 requires parsed datetime inputs")); \
|
||||
return ret_error(b, jv_string("strftime/1 requires parsed datetime inputs"));
|
||||
|
||||
const char *fmt = jv_string_value(b);
|
||||
size_t alloced = strlen(fmt) + 100;
|
||||
char *buf = alloca(alloced);
|
||||
@@ -1205,13 +1495,43 @@ static jv f_strftime(jq_state *jq, jv a, jv b) {
|
||||
return jv_string(buf);
|
||||
}
|
||||
#else
|
||||
static jv f_strftime(jq_state *jq, jv a) {
|
||||
static jv f_strftime(jq_state *jq, jv a, jv b) {
|
||||
jv_free(a);
|
||||
jv_free(b);
|
||||
return jv_invalid_with_msg(jv_string("strftime/1 not implemented on this platform"));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STRFTIME
|
||||
static jv f_strflocaltime(jq_state *jq, jv a, jv b) {
|
||||
if (jv_get_kind(a) == JV_KIND_NUMBER) {
|
||||
a = f_localtime(jq, a);
|
||||
} else if (jv_get_kind(a) != JV_KIND_ARRAY) {
|
||||
return ret_error2(a, b, jv_string("strflocaltime/1 requires parsed datetime inputs"));
|
||||
} else if (jv_get_kind(b) != JV_KIND_STRING) {
|
||||
return ret_error2(a, b, jv_string("strflocaltime/1 requires a string format"));
|
||||
}
|
||||
struct tm tm;
|
||||
if (!jv2tm(a, &tm))
|
||||
return jv_invalid_with_msg(jv_string("strflocaltime/1 requires parsed datetime inputs"));
|
||||
const char *fmt = jv_string_value(b);
|
||||
size_t alloced = strlen(fmt) + 100;
|
||||
char *buf = alloca(alloced);
|
||||
size_t n = strftime(buf, alloced, fmt, &tm);
|
||||
jv_free(b);
|
||||
/* POSIX doesn't provide errno values for strftime() failures; weird */
|
||||
if (n == 0 || n > alloced)
|
||||
return jv_invalid_with_msg(jv_string("strflocaltime/1: unknown system failure"));
|
||||
return jv_string(buf);
|
||||
}
|
||||
#else
|
||||
static jv f_strflocaltime(jq_state *jq, jv a, jv b) {
|
||||
jv_free(a);
|
||||
jv_free(b);
|
||||
return jv_invalid_with_msg(jv_string("strflocaltime/1 not implemented on this platform"));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GETTIMEOFDAY
|
||||
static jv f_now(jq_state *jq, jv a) {
|
||||
jv_free(a);
|
||||
@@ -1227,27 +1547,43 @@ static jv f_now(jq_state *jq, jv a) {
|
||||
}
|
||||
#endif
|
||||
|
||||
static jv f_current_filename(jq_state *jq) {
|
||||
static jv f_current_filename(jq_state *jq, jv a) {
|
||||
jv_free(a);
|
||||
|
||||
jv r = jq_util_input_get_current_filename(jq);
|
||||
if (jv_is_valid(r))
|
||||
return r;
|
||||
jv_free(r);
|
||||
return jv_null();
|
||||
}
|
||||
static jv f_current_line(jq_state *jq) {
|
||||
static jv f_current_line(jq_state *jq, jv a) {
|
||||
jv_free(a);
|
||||
return jq_util_input_get_current_line(jq);
|
||||
}
|
||||
|
||||
#define LIBM_DD(name) \
|
||||
{(cfunction_ptr)f_ ## name, "_" #name, 1},
|
||||
{(cfunction_ptr)f_ ## name, #name, 1},
|
||||
#define LIBM_DD_NO(name)
|
||||
|
||||
#define LIBM_DDD(name) \
|
||||
{(cfunction_ptr)f_ ## name, "_" #name, 3},
|
||||
{(cfunction_ptr)f_ ## name, #name, 3},
|
||||
#define LIBM_DDD_NO(name)
|
||||
|
||||
#define LIBM_DDDD(name) \
|
||||
{(cfunction_ptr)f_ ## name, #name, 4},
|
||||
#define LIBM_DDDD_NO(name)
|
||||
|
||||
static const struct cfunction function_list[] = {
|
||||
#include "libm.h"
|
||||
#ifdef HAVE_FREXP
|
||||
{(cfunction_ptr)f_frexp,"frexp", 1},
|
||||
#endif
|
||||
#ifdef HAVE_MODF
|
||||
{(cfunction_ptr)f_modf,"modf", 1},
|
||||
#endif
|
||||
#ifdef HAVE_LGAMMA_R
|
||||
{(cfunction_ptr)f_lgamma_r,"lgamma_r", 1},
|
||||
#endif
|
||||
{(cfunction_ptr)f_plus, "_plus", 3},
|
||||
{(cfunction_ptr)f_negate, "_negate", 1},
|
||||
{(cfunction_ptr)f_minus, "_minus", 3},
|
||||
@@ -1297,6 +1633,8 @@ static const struct cfunction function_list[] = {
|
||||
{(cfunction_ptr)f_error, "error", 2},
|
||||
{(cfunction_ptr)f_format, "format", 2},
|
||||
{(cfunction_ptr)f_env, "env", 1},
|
||||
{(cfunction_ptr)f_halt, "halt", 1},
|
||||
{(cfunction_ptr)f_halt_error, "halt_error", 2},
|
||||
{(cfunction_ptr)f_get_search_list, "get_search_list", 1},
|
||||
{(cfunction_ptr)f_get_prog_origin, "get_prog_origin", 1},
|
||||
{(cfunction_ptr)f_get_jq_origin, "get_jq_origin", 1},
|
||||
@@ -1307,14 +1645,18 @@ static const struct cfunction function_list[] = {
|
||||
{(cfunction_ptr)f_stderr, "stderr", 1},
|
||||
{(cfunction_ptr)f_strptime, "strptime", 2},
|
||||
{(cfunction_ptr)f_strftime, "strftime", 2},
|
||||
{(cfunction_ptr)f_strflocaltime, "strflocaltime", 2},
|
||||
{(cfunction_ptr)f_mktime, "mktime", 1},
|
||||
{(cfunction_ptr)f_gmtime, "gmtime", 1},
|
||||
{(cfunction_ptr)f_localtime, "localtime", 1},
|
||||
{(cfunction_ptr)f_now, "now", 1},
|
||||
{(cfunction_ptr)f_current_filename, "input_filename", 1},
|
||||
{(cfunction_ptr)f_current_line, "input_line_number", 1},
|
||||
};
|
||||
#undef LIBM_DDDD_NO
|
||||
#undef LIBM_DDD_NO
|
||||
#undef LIBM_DD_NO
|
||||
#undef LIBM_DDDD
|
||||
#undef LIBM_DDD
|
||||
#undef LIBM_DD
|
||||
|
||||
@@ -1361,33 +1703,54 @@ static block bind_bytecoded_builtins(block b) {
|
||||
BLOCK(gen_param("start"), gen_param("end")),
|
||||
range));
|
||||
}
|
||||
|
||||
return block_bind_referenced(builtins, b, OP_IS_CALL_PSEUDO);
|
||||
return block_bind(builtins, b, OP_IS_CALL_PSEUDO);
|
||||
}
|
||||
|
||||
#define LIBM_DD(name) "def " #name ": _" #name ";"
|
||||
#define LIBM_DDD(name) "def " #name "(a;b): _" #name "(a;b);"
|
||||
#define LIBM_DD_NO(name) "def " #name ": \"Error: " #name "() not found at build time\"|error;"
|
||||
#define LIBM_DDD_NO(name) "def " #name "(a;b): \"Error: " #name "() not found at build time\"|error;"
|
||||
|
||||
|
||||
static const char* const jq_builtins =
|
||||
#include "libm.h"
|
||||
/* Include jq-coded builtins */
|
||||
#include "src/builtin.inc"
|
||||
|
||||
/* Include unsupported math functions next */
|
||||
#define LIBM_DD(name)
|
||||
#define LIBM_DDD(name)
|
||||
#define LIBM_DDDD(name)
|
||||
#define LIBM_DD_NO(name) "def " #name ": \"Error: " #name "/0 not found at build time\"|error;"
|
||||
#define LIBM_DDD_NO(name) "def " #name "(a;b): \"Error: " #name "/2 not found at build time\"|error;"
|
||||
#define LIBM_DDDD_NO(name) "def " #name "(a;b;c): \"Error: " #name "/3 not found at build time\"|error;"
|
||||
#include "libm.h"
|
||||
#ifndef HAVE_FREXP
|
||||
"def frexp: \"Error: frexp/0 not found found at build time\"|error;"
|
||||
#endif
|
||||
#ifndef HAVE_MODF
|
||||
"def modf: \"Error: modf/0 not found found at build time\"|error;"
|
||||
#endif
|
||||
#ifndef HAVE_LGAMMA_R
|
||||
"def lgamma_r: \"Error: lgamma_r/0 not found found at build time\"|error;"
|
||||
#endif
|
||||
;
|
||||
|
||||
#undef LIBM_DDDD_NO
|
||||
#undef LIBM_DDD_NO
|
||||
#undef LIBM_DD_NO
|
||||
#undef LIBM_DDDD
|
||||
#undef LIBM_DDD
|
||||
#undef LIBM_DD
|
||||
|
||||
|
||||
static block gen_builtin_list(block builtins) {
|
||||
jv list = jv_array_append(block_list_funcs(builtins, 1), jv_string("builtins/0"));
|
||||
return BLOCK(builtins, gen_function("builtins", gen_noop(), gen_const(list)));
|
||||
}
|
||||
|
||||
static int builtins_bind_one(jq_state *jq, block* bb, const char* code) {
|
||||
struct locfile* src;
|
||||
src = locfile_init(jq, "<builtin>", code, strlen(code));
|
||||
block funcs;
|
||||
int nerrors = jq_parse_library(src, &funcs);
|
||||
if (nerrors == 0) {
|
||||
*bb = block_bind_referenced(funcs, *bb, OP_IS_CALL_PSEUDO);
|
||||
*bb = block_bind(funcs, *bb, OP_IS_CALL_PSEUDO);
|
||||
}
|
||||
locfile_free(src);
|
||||
return nerrors;
|
||||
@@ -1409,14 +1772,18 @@ static int slurp_lib(jq_state *jq, block* bb) {
|
||||
}
|
||||
|
||||
int builtins_bind(jq_state *jq, block* bb) {
|
||||
block builtins = gen_noop();
|
||||
int nerrors = slurp_lib(jq, bb);
|
||||
if (nerrors) {
|
||||
block_free(*bb);
|
||||
return nerrors;
|
||||
}
|
||||
nerrors = builtins_bind_one(jq, bb, jq_builtins);
|
||||
nerrors = builtins_bind_one(jq, &builtins, jq_builtins);
|
||||
assert(!nerrors);
|
||||
*bb = bind_bytecoded_builtins(*bb);
|
||||
*bb = gen_cbinding(function_list, sizeof(function_list)/sizeof(function_list[0]), *bb);
|
||||
builtins = bind_bytecoded_builtins(builtins);
|
||||
builtins = gen_cbinding(function_list, sizeof(function_list)/sizeof(function_list[0]), builtins);
|
||||
builtins = gen_builtin_list(builtins);
|
||||
*bb = block_bind(builtins, *bb, OP_IS_CALL_PSEUDO);
|
||||
*bb = block_drop_unreferenced(*bb);
|
||||
return nerrors;
|
||||
}
|
||||
+29
-7
@@ -1,3 +1,4 @@
|
||||
def halt_error: halt_error(5);
|
||||
def error: error(.);
|
||||
def map(f): [.[] | f];
|
||||
def select(f): if f then . else empty end;
|
||||
@@ -10,11 +11,11 @@ def min_by(f): _min_by_impl(map([f]));
|
||||
def add: reduce .[] as $x (null; . + $x);
|
||||
def del(f): delpaths([path(f)]);
|
||||
def _assign(paths; value): value as $v | reduce path(paths) as $p (.; setpath($p; $v));
|
||||
def _modify(paths; update): reduce path(paths) as $p (.; setpath($p; getpath($p) | update));
|
||||
def _modify(paths; update): reduce path(paths) as $p (.; label $out | (setpath($p; getpath($p) | update) | ., break $out), delpaths([$p]));
|
||||
def map_values(f): .[] |= f;
|
||||
|
||||
# recurse
|
||||
def recurse(f): def r: ., (f | select(. != null) | r); r;
|
||||
def recurse(f): def r: ., (f | r); r;
|
||||
def recurse(f; cond): def r: ., (f | select(cond) | r); r;
|
||||
def recurse: recurse(.[]?);
|
||||
def recurse_down: recurse;
|
||||
@@ -166,8 +167,12 @@ def until(cond; next):
|
||||
def _until:
|
||||
if cond then . else (next|_until) end;
|
||||
_until;
|
||||
def limit($n; exp): if $n < 0 then exp else label $out | foreach exp as $item ([$n, null]; if .[0] < 1 then break $out else [.[0] -1, $item] end; .[1]) end;
|
||||
def first(g): label $out | foreach g as $item ([false, null]; if .[0]==true then break $out else [true, $item] end; .[1]);
|
||||
def limit($n; exp):
|
||||
if $n < 0 then exp
|
||||
else label $out | foreach exp as $item ($n; .-1; $item, if . <= 0 then break $out else empty end)
|
||||
end;
|
||||
def isempty(g): 0 == ((label $go | g | (1, break $go)) // 0);
|
||||
def first(g): label $out | g | ., break $out;
|
||||
def last(g): reduce g as $item (null; $item);
|
||||
def nth($n; g): if $n < 0 then error("nth doesn't support negative indices") else last(limit($n + 1; g)) end;
|
||||
def first: .[0];
|
||||
@@ -210,7 +215,7 @@ def ascii_upcase:
|
||||
|
||||
# Streaming utilities
|
||||
def truncate_stream(stream):
|
||||
. as $n | null | stream | . as $input | if (.[0]|length) > $n then setpath([0];$input[0][1:]) else empty end;
|
||||
. as $n | null | stream | . as $input | if (.[0]|length) > $n then setpath([0];$input[0][$n:]) else empty end;
|
||||
def fromstream(i):
|
||||
foreach i as $item (
|
||||
[null,false,null,false];
|
||||
@@ -235,7 +240,7 @@ def tostream:
|
||||
else
|
||||
# We really need a _streaming_ form of `keys`.
|
||||
# We can use `range` for arrays, but not for objects.
|
||||
keys as $keys |
|
||||
keys_unsorted as $keys |
|
||||
$keys[-1] as $last|
|
||||
((# for each key
|
||||
$keys[] | . as $key |
|
||||
@@ -282,8 +287,25 @@ def bsearch(target):
|
||||
def walk(f):
|
||||
. as $in
|
||||
| if type == "object" then
|
||||
reduce keys[] as $key
|
||||
reduce keys_unsorted[] as $key
|
||||
( {}; . + { ($key): ($in[$key] | walk(f)) } ) | f
|
||||
elif type == "array" then map( walk(f) ) | f
|
||||
else f
|
||||
end;
|
||||
|
||||
# SQL-ish operators here:
|
||||
def INDEX(stream; idx_expr):
|
||||
reduce stream as $row ({};
|
||||
.[$row|idx_expr|
|
||||
if type != "string" then tojson
|
||||
else .
|
||||
end] |= $row);
|
||||
def INDEX(idx_expr): INDEX(.[]; idx_expr);
|
||||
def JOIN($idx; idx_expr):
|
||||
[.[] | [., $idx[idx_expr]]];
|
||||
def JOIN($idx; stream; idx_expr):
|
||||
stream | [., $idx[idx_expr]];
|
||||
def JOIN($idx; stream; idx_expr; join_expr):
|
||||
stream | [., $idx[idx_expr]] | join_expr;
|
||||
def IN(s): reduce (first(select(. == s)) | true) as $v (false; if . or $v then true else false end);
|
||||
def IN(src; s): reduce (src|IN(s)) as $v (false; if . or $v then true else false end);
|
||||
+198
-29
@@ -5,6 +5,7 @@
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include "compile.h"
|
||||
#include "bytecode.h"
|
||||
#include "locfile.h"
|
||||
@@ -158,8 +159,15 @@ block gen_const_global(jv constant, const char *name) {
|
||||
return inst_block(i);
|
||||
}
|
||||
|
||||
block gen_op_pushk_under(jv constant) {
|
||||
assert(opcode_describe(PUSHK_UNDER)->flags & OP_HAS_CONSTANT);
|
||||
inst* i = inst_new(PUSHK_UNDER);
|
||||
i->imm.constant = constant;
|
||||
return inst_block(i);
|
||||
}
|
||||
|
||||
int block_is_const(block b) {
|
||||
return (block_is_single(b) && b.first->op == LOADK);
|
||||
return (block_is_single(b) && (b.first->op == LOADK || b.first->op == PUSHK_UNDER));
|
||||
}
|
||||
|
||||
int block_is_const_inf(block b) {
|
||||
@@ -219,6 +227,10 @@ block gen_op_bound(opcode op, block binder) {
|
||||
return b;
|
||||
}
|
||||
|
||||
block gen_dictpair(block k, block v) {
|
||||
return BLOCK(gen_subexp(k), gen_subexp(v), gen_op_simple(INSERT));
|
||||
}
|
||||
|
||||
|
||||
static void inst_join(inst* a, inst* b) {
|
||||
assert(a && b);
|
||||
@@ -480,6 +492,18 @@ jv block_take_imports(block* body) {
|
||||
return imports;
|
||||
}
|
||||
|
||||
jv block_list_funcs(block body, int omit_underscores) {
|
||||
jv funcs = jv_object(); // Use the keys for set semantics.
|
||||
for (inst *pos = body.first; pos != NULL; pos = pos->next) {
|
||||
if (pos->op == CLOSURE_CREATE || pos->op == CLOSURE_CREATE_C) {
|
||||
if (pos->symbol != NULL && (!omit_underscores || pos->symbol[0] != '_')) {
|
||||
funcs = jv_object_set(funcs, jv_string_fmt("%s/%i", pos->symbol, pos->nformals), jv_null());
|
||||
}
|
||||
}
|
||||
}
|
||||
return jv_keys_unsorted(funcs);
|
||||
}
|
||||
|
||||
block gen_module(block metadata) {
|
||||
inst* i = inst_new(MODULEMETA);
|
||||
i->imm.constant = block_const(metadata);
|
||||
@@ -550,9 +574,15 @@ block gen_call(const char* name, block args) {
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
|
||||
block gen_subexp(block a) {
|
||||
if (block_is_noop(a)) {
|
||||
return gen_op_simple(DUP);
|
||||
}
|
||||
if (block_is_single(a) && a.first->op == LOADK) {
|
||||
jv c = block_const(a);
|
||||
block_free(a);
|
||||
return gen_op_pushk_under(c);
|
||||
}
|
||||
return BLOCK(gen_op_simple(SUBEXP_BEGIN), a, gen_op_simple(SUBEXP_END));
|
||||
}
|
||||
|
||||
@@ -570,17 +600,24 @@ block gen_const_object(block expr) {
|
||||
jv k = jv_null();
|
||||
jv v = jv_null();
|
||||
for (inst *i = expr.first; i; i = i->next) {
|
||||
if (i->op != SUBEXP_BEGIN ||
|
||||
if (i->op == PUSHK_UNDER) {
|
||||
k = jv_copy(i->imm.constant);
|
||||
i = i->next;
|
||||
} else if (i->op != SUBEXP_BEGIN ||
|
||||
i->next == NULL ||
|
||||
i->next->op != LOADK ||
|
||||
i->next->next == NULL ||
|
||||
i->next->next->op != SUBEXP_END) {
|
||||
is_const = 0;
|
||||
break;
|
||||
} else {
|
||||
k = jv_copy(i->next->imm.constant);
|
||||
i = i->next->next->next;
|
||||
}
|
||||
k = jv_copy(i->next->imm.constant);
|
||||
i = i->next->next->next;
|
||||
if (i == NULL ||
|
||||
if (i != NULL && i->op == PUSHK_UNDER) {
|
||||
v = jv_copy(i->imm.constant);
|
||||
i = i->next;
|
||||
} else if (i == NULL ||
|
||||
i->op != SUBEXP_BEGIN ||
|
||||
i->next == NULL ||
|
||||
i->next->op != LOADK ||
|
||||
@@ -588,9 +625,10 @@ block gen_const_object(block expr) {
|
||||
i->next->next->op != SUBEXP_END) {
|
||||
is_const = 0;
|
||||
break;
|
||||
} else {
|
||||
v = jv_copy(i->next->imm.constant);
|
||||
i = i->next->next->next;
|
||||
}
|
||||
v = jv_copy(i->next->imm.constant);
|
||||
i = i->next->next->next;
|
||||
if (i == NULL || i->op != INSERT) {
|
||||
is_const = 0;
|
||||
break;
|
||||
@@ -696,17 +734,91 @@ static block bind_matcher(block matcher, block body) {
|
||||
// block_has_only_binders(matcher), which is not true here as matchers
|
||||
// may also contain code to extract the correct elements
|
||||
for (inst* i = matcher.first; i; i = i->next) {
|
||||
if (i->op == STOREV && !i->bound_by)
|
||||
if ((i->op == STOREV || i->op == STOREVN) && !i->bound_by)
|
||||
block_bind_subblock(inst_block(i), body, OP_HAS_VARIABLE, 0);
|
||||
}
|
||||
return BLOCK(matcher, body);
|
||||
}
|
||||
|
||||
|
||||
// Extract destructuring var names from the block
|
||||
// *vars should be a jv_object (for set semantics)
|
||||
static void block_get_unbound_vars(block b, jv *vars) {
|
||||
assert(vars != NULL);
|
||||
assert(jv_get_kind(*vars) == JV_KIND_OBJECT);
|
||||
for (inst* i = b.first; i; i = i->next) {
|
||||
if (i->subfn.first) {
|
||||
block_get_unbound_vars(i->subfn, vars);
|
||||
continue;
|
||||
}
|
||||
if ((i->op == STOREV || i->op == STOREVN) && i->bound_by == NULL) {
|
||||
*vars = jv_object_set(*vars, jv_string(i->symbol), jv_true());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Build wrappers around destructuring matchers so that we can chain them
|
||||
* when we have errors. The approach is as follows:
|
||||
* DESTRUCTURE_ALT NEXT_MATCHER (unless last matcher)
|
||||
* existing_matcher_block
|
||||
* JUMP BODY
|
||||
*/
|
||||
static block bind_alternation_matchers(block matchers, block body) {
|
||||
block preamble = {0};
|
||||
block altmatchers = {0};
|
||||
block mb = {0};
|
||||
block final_matcher = matchers;
|
||||
|
||||
// Pass through the matchers to find all destructured names.
|
||||
while (final_matcher.first && final_matcher.first->op == DESTRUCTURE_ALT) {
|
||||
block_append(&altmatchers, inst_block(block_take(&final_matcher)));
|
||||
}
|
||||
|
||||
// We don't have any alternations here, so we can use the simplest case.
|
||||
if (altmatchers.first == NULL) {
|
||||
return bind_matcher(final_matcher, body);
|
||||
}
|
||||
|
||||
// Collect var names
|
||||
jv all_vars = jv_object();
|
||||
block_get_unbound_vars(altmatchers, &all_vars);
|
||||
block_get_unbound_vars(final_matcher, &all_vars);
|
||||
|
||||
// We need a preamble of STOREVs to which to bind the matchers and the body.
|
||||
jv_object_keys_foreach(all_vars, key) {
|
||||
preamble = BLOCK(preamble,
|
||||
gen_op_simple(DUP),
|
||||
gen_const(jv_null()),
|
||||
gen_op_unbound(STOREV, jv_string_value(key)));
|
||||
jv_free(key);
|
||||
}
|
||||
jv_free(all_vars);
|
||||
|
||||
// Now we build each matcher in turn
|
||||
for (inst *i = altmatchers.first; i; i = i->next) {
|
||||
block submatcher = i->subfn;
|
||||
|
||||
// If we're successful, jump to the end of the matchers
|
||||
submatcher = BLOCK(submatcher, gen_op_target(JUMP, final_matcher));
|
||||
|
||||
// DESTRUCTURE_ALT to the end of this submatcher so we can skip to the next one on error
|
||||
mb = BLOCK(mb, gen_op_target(DESTRUCTURE_ALT, submatcher), submatcher);
|
||||
|
||||
// We're done with this inst and we don't want it anymore
|
||||
// But we can't let it free the submatcher block.
|
||||
i->subfn.first = i->subfn.last = NULL;
|
||||
}
|
||||
// We're done with these insts now.
|
||||
block_free(altmatchers);
|
||||
|
||||
return bind_matcher(preamble, BLOCK(mb, final_matcher, body));
|
||||
}
|
||||
|
||||
block gen_reduce(block source, block matcher, block init, block body) {
|
||||
block res_var = gen_op_var_fresh(STOREV, "reduce");
|
||||
block loop = BLOCK(gen_op_simple(DUPN),
|
||||
source,
|
||||
bind_matcher(matcher,
|
||||
bind_alternation_matchers(matcher,
|
||||
BLOCK(gen_op_bound(LOADVN, res_var),
|
||||
body,
|
||||
gen_op_bound(STOREV, res_var))),
|
||||
@@ -727,7 +839,7 @@ block gen_foreach(block source, block matcher, block init, block update, block e
|
||||
source,
|
||||
// destructure the value into variable(s) for all the code
|
||||
// in the body to see
|
||||
bind_matcher(matcher,
|
||||
bind_alternation_matchers(matcher,
|
||||
// load the loop state variable
|
||||
BLOCK(gen_op_bound(LOADVN, state_var),
|
||||
// generate updated state
|
||||
@@ -833,6 +945,17 @@ block gen_or(block a, block b) {
|
||||
gen_const(jv_false())))));
|
||||
}
|
||||
|
||||
block gen_destructure_alt(block matcher) {
|
||||
for (inst *i = matcher.first; i; i = i->next) {
|
||||
if (i->op == STOREV) {
|
||||
i->op = STOREVN;
|
||||
}
|
||||
}
|
||||
inst* i = inst_new(DESTRUCTURE_ALT);
|
||||
i->subfn = matcher;
|
||||
return inst_block(i);
|
||||
}
|
||||
|
||||
block gen_var_binding(block var, const char* name, block body) {
|
||||
return gen_destructure(var, gen_op_unbound(STOREV, name), body);
|
||||
}
|
||||
@@ -845,9 +968,16 @@ block gen_array_matcher(block left, block curr) {
|
||||
// `left` was returned by this function, so the third inst is the
|
||||
// constant containing the previously used index
|
||||
assert(left.first->op == DUP);
|
||||
assert(left.first->next->op == SUBEXP_BEGIN);
|
||||
assert(left.first->next->next->op == LOADK);
|
||||
index = 1 + (int) jv_number_value(left.first->next->next->imm.constant);
|
||||
assert(left.first->next != NULL);
|
||||
inst *i = NULL;
|
||||
if (left.first->next->op == PUSHK_UNDER) {
|
||||
i = left.first->next;
|
||||
} else {
|
||||
assert(left.first->next->op == SUBEXP_BEGIN);
|
||||
assert(left.first->next->next->op == LOADK);
|
||||
i = left.first->next->next;
|
||||
}
|
||||
index = 1 + (int) jv_number_value(i->imm.constant);
|
||||
}
|
||||
|
||||
// `left` goes at the end so that the const index is in a predictable place
|
||||
@@ -860,13 +990,19 @@ block gen_object_matcher(block name, block curr) {
|
||||
curr);
|
||||
}
|
||||
|
||||
block gen_destructure(block var, block matcher, block body) {
|
||||
block gen_destructure(block var, block matchers, block body) {
|
||||
// var bindings can be added after coding the program; leave the TOP first.
|
||||
block top = gen_noop();
|
||||
if (body.first && body.first->op == TOP)
|
||||
top = inst_block(block_take(&body));
|
||||
|
||||
return BLOCK(top, gen_op_simple(DUP), var, bind_matcher(matcher, body));
|
||||
if (matchers.first && matchers.first->op == DESTRUCTURE_ALT) {
|
||||
block_append(&var, gen_op_simple(DUP));
|
||||
} else {
|
||||
top = BLOCK(top, gen_op_simple(DUP));
|
||||
}
|
||||
|
||||
return BLOCK(top, gen_subexp(var), gen_op_simple(POP), bind_alternation_matchers(matchers, body));
|
||||
}
|
||||
|
||||
// Like gen_var_binding(), but bind `break`'s wildcard unbound variable
|
||||
@@ -877,7 +1013,7 @@ static block gen_wildvar_binding(block var, const char* name, block body) {
|
||||
}
|
||||
|
||||
block gen_cond(block cond, block iftrue, block iffalse) {
|
||||
return BLOCK(gen_op_simple(DUP), cond,
|
||||
return BLOCK(gen_op_simple(DUP), BLOCK(gen_subexp(cond), gen_op_simple(POP)),
|
||||
gen_condbranch(BLOCK(gen_op_simple(POP), iftrue),
|
||||
BLOCK(gen_op_simple(POP), iffalse)));
|
||||
}
|
||||
@@ -970,16 +1106,46 @@ static int count_cfunctions(block b) {
|
||||
return n;
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
extern char **environ;
|
||||
#endif
|
||||
|
||||
static jv
|
||||
make_env(jv env)
|
||||
{
|
||||
if (jv_is_valid(env))
|
||||
return jv_copy(env);
|
||||
jv r = jv_object();
|
||||
if (environ == NULL)
|
||||
return r;
|
||||
for (size_t i = 0; environ[i] != NULL; i++) {
|
||||
const char *eq;
|
||||
|
||||
if ((eq = strchr(environ[i], '=')) == NULL)
|
||||
r = jv_object_delete(r, jv_string(environ[i]));
|
||||
else
|
||||
r = jv_object_set(r, jv_string_sized(environ[i], eq - environ[i]), jv_string(eq + 1));
|
||||
}
|
||||
return jv_copy(r);
|
||||
}
|
||||
|
||||
// Expands call instructions into a calling sequence
|
||||
static int expand_call_arglist(block* b) {
|
||||
static int expand_call_arglist(block* b, jv args, jv *env) {
|
||||
int errors = 0;
|
||||
block ret = gen_noop();
|
||||
for (inst* curr; (curr = block_take(b));) {
|
||||
if (opcode_describe(curr->op)->flags & OP_HAS_BINDING) {
|
||||
if (!curr->bound_by) {
|
||||
if (!curr->bound_by && curr->op == LOADV && strcmp(curr->symbol, "ENV") == 0) {
|
||||
curr->op = LOADK;
|
||||
*env = curr->imm.constant = make_env(*env);
|
||||
} else if (!curr->bound_by && curr->op == LOADV && jv_object_has(jv_copy(args), jv_string(curr->symbol))) {
|
||||
curr->op = LOADK;
|
||||
curr->imm.constant = jv_object_get(jv_copy(args), jv_string(curr->symbol));
|
||||
} else if (!curr->bound_by) {
|
||||
if (curr->symbol[0] == '*' && curr->symbol[1] >= '1' && curr->symbol[1] <= '3' && curr->symbol[2] == '\0')
|
||||
locfile_locate(curr->locfile, curr->source, "jq: error: break used outside labeled control structure");
|
||||
else if (curr->op == LOADV)
|
||||
locfile_locate(curr->locfile, curr->source, "jq: error: $%s is not defined", curr->symbol);
|
||||
else
|
||||
locfile_locate(curr->locfile, curr->source, "jq: error: %s/%d is not defined", curr->symbol, block_count_actuals(curr->arglist));
|
||||
errors++;
|
||||
@@ -1032,7 +1198,7 @@ static int expand_call_arglist(block* b) {
|
||||
i->subfn = gen_noop();
|
||||
inst_free(i);
|
||||
// arguments should be pushed in reverse order, prepend them to prelude
|
||||
errors += expand_call_arglist(&body);
|
||||
errors += expand_call_arglist(&body, args, env);
|
||||
prelude = BLOCK(gen_subexp(body), prelude);
|
||||
actual_args++;
|
||||
}
|
||||
@@ -1054,12 +1220,12 @@ static int expand_call_arglist(block* b) {
|
||||
return errors;
|
||||
}
|
||||
|
||||
static int compile(struct bytecode* bc, block b, struct locfile* lf) {
|
||||
static int compile(struct bytecode* bc, block b, struct locfile* lf, jv args, jv *env) {
|
||||
int errors = 0;
|
||||
int pos = 0;
|
||||
int var_frame_idx = 0;
|
||||
bc->nsubfunctions = 0;
|
||||
errors += expand_call_arglist(&b);
|
||||
errors += expand_call_arglist(&b, args, env);
|
||||
b = BLOCK(b, gen_op_simple(RET));
|
||||
jv localnames = jv_array();
|
||||
for (inst* curr = b.first; curr; curr = curr->next) {
|
||||
@@ -1104,7 +1270,7 @@ static int compile(struct bytecode* bc, block b, struct locfile* lf) {
|
||||
bc->codelen = pos;
|
||||
bc->debuginfo = jv_object_set(bc->debuginfo, jv_string("locals"), localnames);
|
||||
if (bc->nsubfunctions) {
|
||||
bc->subfunctions = jv_mem_alloc(sizeof(struct bytecode*) * bc->nsubfunctions);
|
||||
bc->subfunctions = jv_mem_calloc(sizeof(struct bytecode*), bc->nsubfunctions);
|
||||
for (inst* curr = b.first; curr; curr = curr->next) {
|
||||
if (curr->op == CLOSURE_CREATE) {
|
||||
struct bytecode* subfn = jv_mem_alloc(sizeof(struct bytecode));
|
||||
@@ -1122,14 +1288,14 @@ static int compile(struct bytecode* bc, block b, struct locfile* lf) {
|
||||
params = jv_array_append(params, jv_string(param->symbol));
|
||||
}
|
||||
subfn->debuginfo = jv_object_set(subfn->debuginfo, jv_string("params"), params);
|
||||
errors += compile(subfn, curr->subfn, lf);
|
||||
errors += compile(subfn, curr->subfn, lf, args, env);
|
||||
curr->subfn = gen_noop();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bc->subfunctions = 0;
|
||||
}
|
||||
uint16_t* code = jv_mem_alloc(sizeof(uint16_t) * bc->codelen);
|
||||
uint16_t* code = jv_mem_calloc(sizeof(uint16_t), bc->codelen);
|
||||
bc->code = code;
|
||||
pos = 0;
|
||||
jv constant_pool = jv_array();
|
||||
@@ -1187,17 +1353,20 @@ static int compile(struct bytecode* bc, block b, struct locfile* lf) {
|
||||
return errors;
|
||||
}
|
||||
|
||||
int block_compile(block b, struct bytecode** out, struct locfile* lf) {
|
||||
int block_compile(block b, struct bytecode** out, struct locfile* lf, jv args) {
|
||||
struct bytecode* bc = jv_mem_alloc(sizeof(struct bytecode));
|
||||
bc->parent = 0;
|
||||
bc->nclosures = 0;
|
||||
bc->globals = jv_mem_alloc(sizeof(struct symbol_table));
|
||||
int ncfunc = count_cfunctions(b);
|
||||
bc->globals->ncfunctions = 0;
|
||||
bc->globals->cfunctions = jv_mem_alloc(sizeof(struct cfunction) * ncfunc);
|
||||
bc->globals->cfunctions = jv_mem_calloc(sizeof(struct cfunction), ncfunc);
|
||||
bc->globals->cfunc_names = jv_array();
|
||||
bc->debuginfo = jv_object_set(jv_object(), jv_string("name"), jv_null());
|
||||
int nerrors = compile(bc, b, lf);
|
||||
jv env = jv_invalid();
|
||||
int nerrors = compile(bc, b, lf, args, &env);
|
||||
jv_free(args);
|
||||
jv_free(env);
|
||||
assert(bc->globals->ncfunctions == ncfunc);
|
||||
if (nerrors > 0) {
|
||||
bytecode_free(bc);
|
||||
|
||||
+5
-1
@@ -29,6 +29,7 @@ block gen_op_target(opcode op, block target);
|
||||
block gen_op_unbound(opcode op, const char* name);
|
||||
block gen_op_bound(opcode op, block binder);
|
||||
block gen_op_var_fresh(opcode op, const char* name);
|
||||
block gen_op_pushk_under(jv constant);
|
||||
|
||||
block gen_module(block metadata);
|
||||
jv block_module_meta(block b);
|
||||
@@ -49,11 +50,13 @@ block gen_definedor(block a, block b);
|
||||
block gen_condbranch(block iftrue, block iffalse);
|
||||
block gen_and(block a, block b);
|
||||
block gen_or(block a, block b);
|
||||
block gen_dictpair(block k, block v);
|
||||
|
||||
block gen_var_binding(block var, const char* name, block body);
|
||||
block gen_array_matcher(block left, block curr);
|
||||
block gen_object_matcher(block name, block curr);
|
||||
block gen_destructure(block var, block matcher, block body);
|
||||
block gen_destructure_alt(block matcher);
|
||||
|
||||
block gen_cond(block cond, block iftrue, block iffalse);
|
||||
block gen_try_handler(block handler);
|
||||
@@ -75,8 +78,9 @@ block block_bind_referenced(block binder, block body, int bindflags);
|
||||
block block_drop_unreferenced(block body);
|
||||
|
||||
jv block_take_imports(block* body);
|
||||
jv block_list_funcs(block body, int omit_underscores);
|
||||
|
||||
int block_compile(block, struct bytecode**, struct locfile*);
|
||||
int block_compile(block, struct bytecode**, struct locfile*, jv);
|
||||
|
||||
void block_free(block);
|
||||
|
||||
|
||||
+124
-23
@@ -40,6 +40,10 @@ struct jq_state {
|
||||
int initial_execution;
|
||||
unsigned next_label;
|
||||
|
||||
int halted;
|
||||
jv exit_code;
|
||||
jv error_message;
|
||||
|
||||
jv attrs;
|
||||
jq_input_cb input_cb;
|
||||
void *input_cb_data;
|
||||
@@ -242,6 +246,28 @@ static void path_append(jq_state* jq, jv component, jv value_at_path) {
|
||||
}
|
||||
}
|
||||
|
||||
/* For f_getpath() */
|
||||
jv
|
||||
_jq_path_append(jq_state *jq, jv v, jv p, jv value_at_path) {
|
||||
if (jq->subexp_nest != 0 ||
|
||||
jv_get_kind(jq->path) != JV_KIND_ARRAY ||
|
||||
!jv_is_valid(value_at_path)) {
|
||||
jv_free(v);
|
||||
jv_free(p);
|
||||
return value_at_path;
|
||||
}
|
||||
if (!jv_identical(v, jv_copy(jq->value_at_path))) {
|
||||
jv_free(p);
|
||||
return value_at_path;
|
||||
}
|
||||
if (jv_get_kind(p) == JV_KIND_ARRAY)
|
||||
jq->path = jv_array_concat(jq->path, p);
|
||||
else
|
||||
jq->path = jv_array_append(jq->path, p);
|
||||
jv_free(jq->value_at_path);
|
||||
return jv_copy(jq->value_at_path = value_at_path);
|
||||
}
|
||||
|
||||
uint16_t* stack_restore(jq_state *jq){
|
||||
while (!stack_pop_will_free(&jq->stk, jq->fork_top)) {
|
||||
if (stack_pop_will_free(&jq->stk, jq->stk_top)) {
|
||||
@@ -285,6 +311,9 @@ static void jq_reset(jq_state *jq) {
|
||||
jv_free(jq->error);
|
||||
jq->error = jv_null();
|
||||
|
||||
jq->halted = 0;
|
||||
jv_free(jq->exit_code);
|
||||
jv_free(jq->error_message);
|
||||
if (jv_get_kind(jq->path) != JV_KIND_INVALID)
|
||||
jv_free(jq->path);
|
||||
jq->path = jv_null();
|
||||
@@ -320,6 +349,11 @@ jv jq_next(jq_state *jq) {
|
||||
jq->initial_execution = 0;
|
||||
assert(jv_get_kind(jq->error) == JV_KIND_NULL);
|
||||
while (1) {
|
||||
if (jq->halted) {
|
||||
if (jq->debug_trace_enabled)
|
||||
printf("\t<halted>\n");
|
||||
return jv_invalid();
|
||||
}
|
||||
uint16_t opcode = *pc;
|
||||
raising = 0;
|
||||
|
||||
@@ -331,10 +365,9 @@ jv jq_next(jq_state *jq) {
|
||||
if (!backtracking) {
|
||||
int stack_in = opdesc->stack_in;
|
||||
if (stack_in == -1) stack_in = pc[1];
|
||||
param = jq->stk_top;
|
||||
for (int i=0; i<stack_in; i++) {
|
||||
if (i == 0) {
|
||||
param = jq->stk_top;
|
||||
} else {
|
||||
if (i != 0) {
|
||||
printf(" | ");
|
||||
param = *stack_block_next(&jq->stk, param);
|
||||
}
|
||||
@@ -344,6 +377,12 @@ jv jq_next(jq_state *jq) {
|
||||
//printf(" -- ");
|
||||
//jv_dump(jv_copy(jq->path), 0);
|
||||
}
|
||||
if (jq->debug_trace_enabled & JQ_DEBUG_TRACE_DETAIL) {
|
||||
while ((param = *stack_block_next(&jq->stk, param))) {
|
||||
printf(" || ");
|
||||
jv_dump(jv_copy(*(jv*)stack_block(&jq->stk, param)), JV_PRINT_REFCOUNT);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
printf("\t<backtracking>");
|
||||
}
|
||||
@@ -417,6 +456,15 @@ jv jq_next(jq_state *jq) {
|
||||
break;
|
||||
}
|
||||
|
||||
case PUSHK_UNDER: {
|
||||
jv v = jv_array_get(jv_copy(frame_current(jq)->bc->constants), *pc++);
|
||||
assert(jv_is_valid(v));
|
||||
jv v2 = stack_pop(jq);
|
||||
stack_push(jq, v);
|
||||
stack_push(jq, v2);
|
||||
break;
|
||||
}
|
||||
|
||||
case POP: {
|
||||
jv_free(stack_pop(jq));
|
||||
break;
|
||||
@@ -514,6 +562,8 @@ jv jq_next(jq_state *jq) {
|
||||
break;
|
||||
}
|
||||
|
||||
case STOREVN:
|
||||
stack_save(jq, pc - 1, stack_get_pos(jq));
|
||||
case STOREV: {
|
||||
uint16_t level = *pc++;
|
||||
uint16_t v = *pc++;
|
||||
@@ -529,6 +579,16 @@ jv jq_next(jq_state *jq) {
|
||||
break;
|
||||
}
|
||||
|
||||
case ON_BACKTRACK(STOREVN): {
|
||||
uint16_t level = *pc++;
|
||||
uint16_t v = *pc++;
|
||||
jv* var = frame_local_var(jq, v, level);
|
||||
jv_free(*var);
|
||||
*var = jv_null();
|
||||
goto do_backtrack;
|
||||
break;
|
||||
}
|
||||
|
||||
case STORE_GLOBAL: {
|
||||
// Get the constant
|
||||
jv val = jv_array_get(jv_copy(frame_current(jq)->bc->constants), *pc++);
|
||||
@@ -739,21 +799,27 @@ jv jq_next(jq_state *jq) {
|
||||
}
|
||||
|
||||
case FORK_OPT:
|
||||
case DESTRUCTURE_ALT:
|
||||
case FORK: {
|
||||
stack_save(jq, pc - 1, stack_get_pos(jq));
|
||||
pc++; // skip offset this time
|
||||
break;
|
||||
}
|
||||
|
||||
case ON_BACKTRACK(FORK_OPT): {
|
||||
case ON_BACKTRACK(FORK_OPT):
|
||||
case ON_BACKTRACK(DESTRUCTURE_ALT): {
|
||||
if (jv_is_valid(jq->error)) {
|
||||
// `try EXP ...` backtracked here (no value, `empty`), so we backtrack more
|
||||
jv_free(stack_pop(jq));
|
||||
goto do_backtrack;
|
||||
}
|
||||
// `try EXP ...` exception caught in EXP
|
||||
jv_free(stack_pop(jq)); // free the input
|
||||
stack_push(jq, jv_invalid_get_msg(jq->error)); // push the error's message
|
||||
// DESTRUCTURE_ALT doesn't want the error message on the stack,
|
||||
// as we would just want to throw it away anyway.
|
||||
if (opcode != ON_BACKTRACK(DESTRUCTURE_ALT)) {
|
||||
jv_free(stack_pop(jq)); // free the input
|
||||
stack_push(jq, jv_invalid_get_msg(jq->error)); // push the error's message
|
||||
}
|
||||
jq->error = jv_null();
|
||||
uint16_t offset = *pc++;
|
||||
pc += offset;
|
||||
@@ -932,6 +998,10 @@ jq_state *jq_init(void) {
|
||||
jq->curr_frame = 0;
|
||||
jq->error = jv_null();
|
||||
|
||||
jq->halted = 0;
|
||||
jq->exit_code = jv_invalid();
|
||||
jq->error_message = jv_invalid();
|
||||
|
||||
jq->err_cb = default_err_cb;
|
||||
jq->err_cb_data = stderr;
|
||||
|
||||
@@ -974,11 +1044,7 @@ void jq_start(jq_state *jq, jv input, int flags) {
|
||||
|
||||
stack_push(jq, input);
|
||||
stack_save(jq, jq->bc->code, stack_get_pos(jq));
|
||||
if (flags & JQ_DEBUG_TRACE) {
|
||||
jq->debug_trace_enabled = 1;
|
||||
} else {
|
||||
jq->debug_trace_enabled = 0;
|
||||
}
|
||||
jq->debug_trace_enabled = flags & JQ_DEBUG_TRACE_ALL;
|
||||
jq->initial_execution = 1;
|
||||
}
|
||||
|
||||
@@ -1066,9 +1132,26 @@ static struct bytecode *optimize(struct bytecode *bc) {
|
||||
return optimize_code(bc);
|
||||
}
|
||||
|
||||
static jv
|
||||
args2obj(jv args)
|
||||
{
|
||||
if (jv_get_kind(args) == JV_KIND_OBJECT)
|
||||
return args;
|
||||
assert(jv_get_kind(args) == JV_KIND_ARRAY);
|
||||
jv r = jv_object();
|
||||
jv kk = jv_string("name");
|
||||
jv vk = jv_string("value");
|
||||
jv_array_foreach(args, i, v)
|
||||
r = jv_object_set(r, jv_object_get(jv_copy(v), kk), jv_object_get(v, vk));
|
||||
jv_free(args);
|
||||
jv_free(kk);
|
||||
jv_free(vk);
|
||||
return r;
|
||||
}
|
||||
|
||||
int jq_compile_args(jq_state *jq, const char* str, jv args) {
|
||||
jv_nomem_handler(jq->nomem_handler, jq->nomem_handler_data);
|
||||
assert(jv_get_kind(args) == JV_KIND_ARRAY);
|
||||
assert(jv_get_kind(args) == JV_KIND_ARRAY || jv_get_kind(args) == JV_KIND_OBJECT);
|
||||
struct locfile* locations;
|
||||
locations = locfile_init(jq, "<top-level>", str, strlen(str));
|
||||
block program;
|
||||
@@ -1079,29 +1162,22 @@ int jq_compile_args(jq_state *jq, const char* str, jv args) {
|
||||
}
|
||||
int nerrors = load_program(jq, locations, &program);
|
||||
if (nerrors == 0) {
|
||||
jv_array_foreach(args, i, arg) {
|
||||
jv name = jv_object_get(jv_copy(arg), jv_string("name"));
|
||||
jv value = jv_object_get(arg, jv_string("value"));
|
||||
program = gen_var_binding(gen_const(value), jv_string_value(name), program);
|
||||
jv_free(name);
|
||||
}
|
||||
|
||||
nerrors = builtins_bind(jq, &program);
|
||||
if (nerrors == 0) {
|
||||
nerrors = block_compile(program, &jq->bc, locations);
|
||||
nerrors = block_compile(program, &jq->bc, locations, args = args2obj(args));
|
||||
}
|
||||
}
|
||||
} else
|
||||
jv_free(args);
|
||||
if (nerrors)
|
||||
jq_report_error(jq, jv_string_fmt("jq: %d compile %s", nerrors, nerrors > 1 ? "errors" : "error"));
|
||||
if (jq->bc)
|
||||
jq->bc = optimize(jq->bc);
|
||||
jv_free(args);
|
||||
locfile_free(locations);
|
||||
return jq->bc != NULL;
|
||||
}
|
||||
|
||||
int jq_compile(jq_state *jq, const char* str) {
|
||||
return jq_compile_args(jq, str, jv_array());
|
||||
return jq_compile_args(jq, str, jv_object());
|
||||
}
|
||||
|
||||
jv jq_get_jq_origin(jq_state *jq) {
|
||||
@@ -1153,3 +1229,28 @@ void jq_get_debug_cb(jq_state *jq, jq_msg_cb *cb, void **data) {
|
||||
*cb = jq->debug_cb;
|
||||
*data = jq->debug_cb_data;
|
||||
}
|
||||
|
||||
void
|
||||
jq_halt(jq_state *jq, jv exit_code, jv error_message)
|
||||
{
|
||||
assert(!jq->halted);
|
||||
jq->halted = 1;
|
||||
jq->exit_code = exit_code;
|
||||
jq->error_message = error_message;
|
||||
}
|
||||
|
||||
int
|
||||
jq_halted(jq_state *jq)
|
||||
{
|
||||
return jq->halted;
|
||||
}
|
||||
|
||||
jv jq_get_exit_code(jq_state *jq)
|
||||
{
|
||||
return jv_copy(jq->exit_code);
|
||||
}
|
||||
|
||||
jv jq_get_error_message(jq_state *jq)
|
||||
{
|
||||
return jv_copy(jq->error_message);
|
||||
}
|
||||
@@ -4,7 +4,11 @@
|
||||
#include <stdio.h>
|
||||
#include "jv.h"
|
||||
|
||||
enum {JQ_DEBUG_TRACE = 1};
|
||||
enum {
|
||||
JQ_DEBUG_TRACE = 1,
|
||||
JQ_DEBUG_TRACE_DETAIL = 2,
|
||||
JQ_DEBUG_TRACE_ALL = JQ_DEBUG_TRACE | JQ_DEBUG_TRACE_DETAIL,
|
||||
};
|
||||
|
||||
typedef struct jq_state jq_state;
|
||||
typedef void (*jq_msg_cb)(void *, jv);
|
||||
@@ -22,6 +26,11 @@ void jq_start(jq_state *, jv value, int);
|
||||
jv jq_next(jq_state *);
|
||||
void jq_teardown(jq_state **);
|
||||
|
||||
void jq_halt(jq_state *, jv, jv);
|
||||
int jq_halted(jq_state *);
|
||||
jv jq_get_exit_code(jq_state *);
|
||||
jv jq_get_error_message(jq_state *);
|
||||
|
||||
typedef jv (*jq_input_cb)(jq_state *, void *);
|
||||
void jq_set_input_cb(jq_state *, jq_input_cb, void *);
|
||||
void jq_get_input_cb(jq_state *, jq_input_cb *, void **);
|
||||
@@ -57,4 +66,6 @@ jv jq_util_input_get_position(jq_state*);
|
||||
jv jq_util_input_get_current_filename(jq_state*);
|
||||
jv jq_util_input_get_current_line(jq_state*);
|
||||
|
||||
int jq_set_colors(const char *);
|
||||
|
||||
#endif /* !JQ_H */
|
||||
@@ -296,10 +296,18 @@ static jv jvp_array_slice(jv a, int start, int end) {
|
||||
jv_free(a);
|
||||
return jv_array();
|
||||
}
|
||||
// FIXME FIXME FIXME large offsets
|
||||
a.offset += start;
|
||||
a.size = end - start;
|
||||
return a;
|
||||
|
||||
if (a.offset + start >= 1 << (sizeof(a.offset) * CHAR_BIT)) {
|
||||
jv r = jv_array_sized(end - start);
|
||||
for (int i = start; i < end; i++)
|
||||
r = jv_array_append(r, jv_array_get(jv_copy(a), i));
|
||||
jv_free(a);
|
||||
return r;
|
||||
} else {
|
||||
a.offset += start;
|
||||
a.size = end - start;
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1106,6 +1114,16 @@ jv jv_object_get(jv object, jv key) {
|
||||
return val;
|
||||
}
|
||||
|
||||
int jv_object_has(jv object, jv key) {
|
||||
assert(jv_get_kind(object) == JV_KIND_OBJECT);
|
||||
assert(jv_get_kind(key) == JV_KIND_STRING);
|
||||
jv* slot = jvp_object_read(object, key);
|
||||
int res = slot ? 1 : 0;
|
||||
jv_free(object);
|
||||
jv_free(key);
|
||||
return res;
|
||||
}
|
||||
|
||||
jv jv_object_set(jv object, jv key, jv value) {
|
||||
assert(jv_get_kind(object) == JV_KIND_OBJECT);
|
||||
assert(jv_get_kind(key) == JV_KIND_STRING);
|
||||
|
||||
@@ -123,6 +123,7 @@ jv jv_string_implode(jv j);
|
||||
|
||||
jv jv_object(void);
|
||||
jv jv_object_get(jv object, jv key);
|
||||
int jv_object_has(jv object, jv key);
|
||||
jv jv_object_set(jv object, jv key, jv value);
|
||||
jv jv_object_delete(jv object, jv key);
|
||||
int jv_object_length(jv object);
|
||||
@@ -144,6 +145,15 @@ jv jv_object_iter_value(jv, int);
|
||||
: 0; \
|
||||
jv_i__ = jv_object_iter_next(t, jv_i__)) \
|
||||
|
||||
#define jv_object_keys_foreach(t, k) \
|
||||
for (int jv_i__ = jv_object_iter(t), jv_j__ = 1; jv_j__; jv_j__ = 0) \
|
||||
for (jv k; \
|
||||
jv_object_iter_valid((t), jv_i__) ? \
|
||||
(k = jv_object_iter_key(t, jv_i__), \
|
||||
1) \
|
||||
: 0; \
|
||||
jv_i__ = jv_object_iter_next(t, jv_i__))
|
||||
|
||||
#define JV_OBJECT_1(k1) (jv_object_set(jv_object(),(k1),jv_null()))
|
||||
#define JV_OBJECT_2(k1,v1) (jv_object_set(jv_object(),(k1),(v1)))
|
||||
#define JV_OBJECT_3(k1,v1,k2) (jv_object_set(JV_OBJECT_2((k1),(v1)),(k2),jv_null()))
|
||||
|
||||
+2
-2
@@ -465,7 +465,7 @@ jv jv_keys_unsorted(jv x) {
|
||||
jv jv_keys(jv x) {
|
||||
if (jv_get_kind(x) == JV_KIND_OBJECT) {
|
||||
int nkeys = jv_object_length(jv_copy(x));
|
||||
jv* keys = jv_mem_alloc(sizeof(jv) * nkeys);
|
||||
jv* keys = jv_mem_calloc(sizeof(jv), nkeys);
|
||||
int kidx = 0;
|
||||
jv_object_foreach(x, key, value) {
|
||||
keys[kidx++] = key;
|
||||
@@ -587,7 +587,7 @@ static struct sort_entry* sort_items(jv objects, jv keys) {
|
||||
assert(jv_get_kind(keys) == JV_KIND_ARRAY);
|
||||
assert(jv_array_length(jv_copy(objects)) == jv_array_length(jv_copy(keys)));
|
||||
int n = jv_array_length(jv_copy(objects));
|
||||
struct sort_entry* entries = jv_mem_alloc(sizeof(struct sort_entry) * n);
|
||||
struct sort_entry* entries = jv_mem_calloc(sizeof(struct sort_entry), n);
|
||||
for (int i=0; i<n; i++) {
|
||||
entries[i].object = jv_array_get(jv_copy(objects), i);
|
||||
entries[i].key = jv_array_get(jv_copy(keys), i);
|
||||
|
||||
+37
-5
@@ -1,15 +1,33 @@
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "jv.h"
|
||||
#include "jv_unicode.h"
|
||||
|
||||
jv jv_load_file(const char* filename, int raw) {
|
||||
FILE* file = fopen(filename, "r");
|
||||
struct jv_parser* parser;
|
||||
struct stat sb;
|
||||
int fd = open(filename, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
return jv_invalid_with_msg(jv_string_fmt("Could not open %s: %s",
|
||||
filename,
|
||||
strerror(errno)));
|
||||
}
|
||||
if (fstat(fd, &sb) == -1 || S_ISDIR(sb.st_mode)) {
|
||||
close(fd);
|
||||
return jv_invalid_with_msg(jv_string_fmt("Could not open %s: %s",
|
||||
filename,
|
||||
"It's a directory"));
|
||||
}
|
||||
FILE* file = fdopen(fd, "r");
|
||||
struct jv_parser* parser = NULL;
|
||||
jv data;
|
||||
if (!file) {
|
||||
close(fd);
|
||||
return jv_invalid_with_msg(jv_string_fmt("Could not open %s: %s",
|
||||
filename,
|
||||
strerror(errno)));
|
||||
@@ -20,11 +38,25 @@ jv jv_load_file(const char* filename, int raw) {
|
||||
data = jv_array();
|
||||
parser = jv_parser_new(0);
|
||||
}
|
||||
|
||||
// To avoid mangling UTF-8 multi-byte sequences that cross the end of our read
|
||||
// buffer, we need to be able to read the remainder of a sequence and add that
|
||||
// before appending.
|
||||
const int max_utf8_len = 4;
|
||||
char buf[4096+max_utf8_len];
|
||||
while (!feof(file) && !ferror(file)) {
|
||||
char buf[4096];
|
||||
size_t n = fread(buf, 1, sizeof(buf), file);
|
||||
size_t n = fread(buf, 1, sizeof(buf)-max_utf8_len, file);
|
||||
int len = 0;
|
||||
|
||||
if (n == 0)
|
||||
continue;
|
||||
if (jvp_utf8_backtrack(buf+(n-1), buf, &len) && len > 0 &&
|
||||
!feof(file) && !ferror(file)) {
|
||||
n += fread(buf+n, 1, len, file);
|
||||
}
|
||||
|
||||
if (raw) {
|
||||
data = jv_string_concat(data, jv_string_sized(buf, (int)n));
|
||||
data = jv_string_append_buf(data, buf, n);
|
||||
} else {
|
||||
jv_parser_set_buf(parser, buf, n, !feof(file));
|
||||
jv value;
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
|
||||
typedef const char* presult;
|
||||
|
||||
#ifndef MAX_PARSING_DEPTH
|
||||
#define MAX_PARSING_DEPTH (256)
|
||||
#endif
|
||||
|
||||
#define TRY(x) do {presult msg__ = (x); if (msg__) return msg__; } while(0)
|
||||
#ifdef __GNUC__
|
||||
#define pfunc __attribute__((warn_unused_result)) presult
|
||||
@@ -147,11 +151,13 @@ static void push(struct jv_parser* p, jv v) {
|
||||
static pfunc parse_token(struct jv_parser* p, char ch) {
|
||||
switch (ch) {
|
||||
case '[':
|
||||
if (p->stackpos >= MAX_PARSING_DEPTH) return "Exceeds depth limit for parsing";
|
||||
if (jv_is_valid(p->next)) return "Expected separator between values";
|
||||
push(p, jv_array());
|
||||
break;
|
||||
|
||||
case '{':
|
||||
if (p->stackpos >= MAX_PARSING_DEPTH) return "Exceeds depth limit for parsing";
|
||||
if (jv_is_valid(p->next)) return "Expected separator between values";
|
||||
push(p, jv_object());
|
||||
break;
|
||||
|
||||
+58
-5
@@ -12,6 +12,11 @@
|
||||
#include "jv.h"
|
||||
#include "jv_dtoa.h"
|
||||
#include "jv_unicode.h"
|
||||
#include "jv_alloc.h"
|
||||
|
||||
#ifndef MAX_PRINT_DEPTH
|
||||
#define MAX_PRINT_DEPTH (256)
|
||||
#endif
|
||||
|
||||
#define ESC "\033"
|
||||
#define COL(c) (ESC "[" c "m")
|
||||
@@ -22,20 +27,66 @@
|
||||
static const jv_kind color_kinds[] =
|
||||
{JV_KIND_NULL, JV_KIND_FALSE, JV_KIND_TRUE, JV_KIND_NUMBER,
|
||||
JV_KIND_STRING, JV_KIND_ARRAY, JV_KIND_OBJECT};
|
||||
static const char* const colors[] =
|
||||
static char color_bufs[sizeof(color_kinds)/sizeof(color_kinds[0])][16];
|
||||
static const char *color_bufps[8];
|
||||
static const char* def_colors[] =
|
||||
{COL("1;30"), COL("0;39"), COL("0;39"), COL("0;39"),
|
||||
COL("0;32"), COL("1;39"), COL("1;39")};
|
||||
#define FIELD_COLOR COL("34;1")
|
||||
|
||||
static const char **colors = def_colors;
|
||||
|
||||
int
|
||||
jq_set_colors(const char *c)
|
||||
{
|
||||
const char *e;
|
||||
size_t i;
|
||||
|
||||
if (c == NULL)
|
||||
return 1;
|
||||
colors = def_colors;
|
||||
memset(color_bufs, 0, sizeof(color_bufs));
|
||||
for (i = 0; i < sizeof(def_colors) / sizeof(def_colors[0]); i++)
|
||||
color_bufps[i] = def_colors[i];
|
||||
for (i = 0; i < sizeof(def_colors) / sizeof(def_colors[0]) && *c != '\0'; i++, c = e) {
|
||||
if ((e = strchr(c, ':')) == NULL)
|
||||
e = c + strlen(c);
|
||||
if ((size_t)(e - c) > sizeof(color_bufs[i]) - 4 /* ESC [ m NUL */)
|
||||
return 0;
|
||||
color_bufs[i][0] = ESC[0];
|
||||
color_bufs[i][1] = '[';
|
||||
(void) strncpy(&color_bufs[i][2], c, e - c);
|
||||
if (strspn(&color_bufs[i][2], "0123456789;") < strlen(&color_bufs[i][2]))
|
||||
return 0;
|
||||
color_bufs[i][2 + (e - c)] = 'm';
|
||||
color_bufps[i] = color_bufs[i];
|
||||
if (e[0] == ':')
|
||||
e++;
|
||||
}
|
||||
colors = color_bufps;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void put_buf(const char *s, int len, FILE *fout, jv *strout, int is_tty) {
|
||||
if (strout) {
|
||||
*strout = jv_string_append_buf(*strout, s, len);
|
||||
} else {
|
||||
#ifdef WIN32
|
||||
/* See util.h */
|
||||
if (is_tty)
|
||||
WriteFile((HANDLE)_get_osfhandle(fileno(fout)), s, len, NULL, NULL);
|
||||
else
|
||||
if (is_tty) {
|
||||
wchar_t *ws;
|
||||
size_t wl;
|
||||
if (len == -1)
|
||||
len = strlen(s);
|
||||
wl = MultiByteToWideChar(CP_UTF8, 0, s, len, NULL, 0);
|
||||
ws = jv_mem_calloc((wl + 1), sizeof(*ws));
|
||||
if (!ws)
|
||||
return;
|
||||
wl = MultiByteToWideChar(CP_UTF8, 0, s, len, ws, wl + 1);
|
||||
ws[wl] = 0;
|
||||
WriteConsoleW((HANDLE)_get_osfhandle(fileno(fout)), ws, wl, NULL, NULL);
|
||||
free(ws);
|
||||
} else
|
||||
fwrite(s, 1, len, fout);
|
||||
#else
|
||||
fwrite(s, 1, len, fout);
|
||||
@@ -150,7 +201,9 @@ static void jv_dump_term(struct dtoa_context* C, jv x, int flags, int indent, FI
|
||||
}
|
||||
}
|
||||
}
|
||||
switch (jv_get_kind(x)) {
|
||||
if (indent > MAX_PRINT_DEPTH) {
|
||||
put_str("<skipped: too deep>", F, S, flags & JV_PRINT_ISATTY);
|
||||
} else switch (jv_get_kind(x)) {
|
||||
default:
|
||||
case JV_KIND_INVALID:
|
||||
if (flags & JV_PRINT_INVALID) {
|
||||
|
||||
@@ -3,6 +3,29 @@
|
||||
#include "jv_unicode.h"
|
||||
#include "jv_utf8_tables.h"
|
||||
|
||||
// jvp_utf8_backtrack returns the beginning of the last codepoint in the
|
||||
// string, assuming that start is the last byte in the string.
|
||||
// If the last codepoint is incomplete, returns the number of missing bytes via
|
||||
// *missing_bytes. If there are no leading bytes or an invalid byte is
|
||||
// encountered, NULL is returned and *missing_bytes is not altered.
|
||||
const char* jvp_utf8_backtrack(const char* start, const char* min, int *missing_bytes) {
|
||||
assert(min <= start);
|
||||
if (min == start) {
|
||||
return min;
|
||||
}
|
||||
int length = 0;
|
||||
int seen = 1;
|
||||
while (start >= min && (length = utf8_coding_length[(unsigned char)*start]) == UTF8_CONTINUATION_BYTE) {
|
||||
start--;
|
||||
seen++;
|
||||
}
|
||||
if (length == 0 || length == UTF8_CONTINUATION_BYTE || length - seen < 0) {
|
||||
return NULL;
|
||||
}
|
||||
if (missing_bytes) *missing_bytes = length - seen;
|
||||
return start;
|
||||
}
|
||||
|
||||
const char* jvp_utf8_next(const char* in, const char* end, int* codepoint_ret) {
|
||||
assert(in <= end);
|
||||
if (in == end) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef JV_UNICODE_H
|
||||
#define JV_UNICODE_H
|
||||
|
||||
const char* jvp_utf8_backtrack(const char* start, const char* min, int *missing_bytes);
|
||||
const char* jvp_utf8_next(const char* in, const char* end, int* codepoint);
|
||||
int jvp_utf8_is_valid(const char* in, const char* end);
|
||||
|
||||
|
||||
+307
-282
File diff suppressed because it is too large.
Load diff
+16
-12
@@ -2,9 +2,9 @@
|
||||
#define jq_yyHEADER_H 1
|
||||
#define jq_yyIN_HEADER 1
|
||||
|
||||
#line 6 "lexer.h"
|
||||
#line 6 "src/lexer.h"
|
||||
|
||||
#line 8 "lexer.h"
|
||||
#line 8 "src/lexer.h"
|
||||
|
||||
#define YY_INT_ALIGNED short int
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
|
||||
#define FLEX_SCANNER
|
||||
#define YY_FLEX_MAJOR_VERSION 2
|
||||
#define YY_FLEX_MINOR_VERSION 5
|
||||
#define YY_FLEX_SUBMINOR_VERSION 35
|
||||
#define YY_FLEX_MINOR_VERSION 6
|
||||
#define YY_FLEX_SUBMINOR_VERSION 0
|
||||
#if YY_FLEX_SUBMINOR_VERSION > 0
|
||||
#define FLEX_BETA
|
||||
#endif
|
||||
@@ -214,7 +214,7 @@ void jq_yypop_buffer_state (yyscan_t yyscanner );
|
||||
|
||||
YY_BUFFER_STATE jq_yy_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
|
||||
YY_BUFFER_STATE jq_yy_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
|
||||
YY_BUFFER_STATE jq_yy_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner );
|
||||
YY_BUFFER_STATE jq_yy_scan_bytes (yyconst char *bytes,yy_size_t len ,yyscan_t yyscanner );
|
||||
|
||||
void *jq_yyalloc (yy_size_t ,yyscan_t yyscanner );
|
||||
void *jq_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
|
||||
@@ -222,7 +222,7 @@ void jq_yyfree (void * ,yyscan_t yyscanner );
|
||||
|
||||
/* Begin user sect3 */
|
||||
|
||||
#define jq_yywrap(n) 1
|
||||
#define jq_yywrap(yyscanner) (/*CONSTCOND*/1)
|
||||
#define YY_SKIP_YYWRAP
|
||||
|
||||
#define yytext_ptr yytext_r
|
||||
@@ -266,19 +266,23 @@ void jq_yyset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
|
||||
|
||||
FILE *jq_yyget_in (yyscan_t yyscanner );
|
||||
|
||||
void jq_yyset_in (FILE * in_str ,yyscan_t yyscanner );
|
||||
void jq_yyset_in (FILE * _in_str ,yyscan_t yyscanner );
|
||||
|
||||
FILE *jq_yyget_out (yyscan_t yyscanner );
|
||||
|
||||
void jq_yyset_out (FILE * out_str ,yyscan_t yyscanner );
|
||||
void jq_yyset_out (FILE * _out_str ,yyscan_t yyscanner );
|
||||
|
||||
int jq_yyget_leng (yyscan_t yyscanner );
|
||||
yy_size_t jq_yyget_leng (yyscan_t yyscanner );
|
||||
|
||||
char *jq_yyget_text (yyscan_t yyscanner );
|
||||
|
||||
int jq_yyget_lineno (yyscan_t yyscanner );
|
||||
|
||||
void jq_yyset_lineno (int line_number ,yyscan_t yyscanner );
|
||||
void jq_yyset_lineno (int _line_number ,yyscan_t yyscanner );
|
||||
|
||||
int jq_yyget_column (yyscan_t yyscanner );
|
||||
|
||||
void jq_yyset_column (int _column_no ,yyscan_t yyscanner );
|
||||
|
||||
YYSTYPE * jq_yyget_lval (yyscan_t yyscanner );
|
||||
|
||||
@@ -354,9 +358,9 @@ extern int jq_yylex \
|
||||
#undef YY_DECL
|
||||
#endif
|
||||
|
||||
#line 129 "lexer.l"
|
||||
#line 130 "src/lexer.l"
|
||||
|
||||
|
||||
#line 361 "lexer.h"
|
||||
#line 365 "src/lexer.h"
|
||||
#undef jq_yyIN_HEADER
|
||||
#endif /* jq_yyHEADER_H */
|
||||
@@ -71,6 +71,7 @@ struct lexer_param;
|
||||
"<=" { return LESSEQ; }
|
||||
">=" { return GREATEREQ; }
|
||||
".." { return REC; }
|
||||
"?//" { return ALTERNATION; }
|
||||
"."|"?"|"="|";"|","|":"|"|"|"+"|"-"|"*"|"/"|"%"|"\$"|"<"|">" { return yytext[0];}
|
||||
|
||||
"["|"{"|"(" {
|
||||
|
||||
+142
-2
@@ -144,8 +144,148 @@ LIBM_DD(y1)
|
||||
LIBM_DD_NO(y1)
|
||||
#endif
|
||||
#ifdef HAVE_JN
|
||||
LIBM_DID(jn)
|
||||
LIBM_DDD(jn)
|
||||
#endif
|
||||
#ifdef HAVE_YN
|
||||
LIBM_DID(yn)
|
||||
LIBM_DDD(yn)
|
||||
#endif
|
||||
#ifdef HAVE_CEIL
|
||||
LIBM_DD(ceil)
|
||||
#else
|
||||
LIBM_DD_NO(ceil)
|
||||
#endif
|
||||
#ifdef HAVE_COPYSIGN
|
||||
LIBM_DDD(copysign)
|
||||
#else
|
||||
LIBM_DDD_NO(copysign)
|
||||
#endif
|
||||
#if defined(HAVE_DREM) && !defined(WIN32)
|
||||
LIBM_DDD(drem)
|
||||
#else
|
||||
LIBM_DDD_NO(drem)
|
||||
#endif
|
||||
#ifdef HAVE_ERF
|
||||
LIBM_DD(erf)
|
||||
#else
|
||||
LIBM_DD_NO(erf)
|
||||
#endif
|
||||
#ifdef HAVE_ERFC
|
||||
LIBM_DD(erfc)
|
||||
#else
|
||||
LIBM_DD_NO(erfc)
|
||||
#endif
|
||||
#if defined(HAVE_EXP10) && !defined(WIN32)
|
||||
LIBM_DD(exp10)
|
||||
#else
|
||||
LIBM_DD_NO(exp10)
|
||||
#endif
|
||||
#ifdef HAVE_EXPM1
|
||||
LIBM_DD(expm1)
|
||||
#else
|
||||
LIBM_DD_NO(expm1)
|
||||
#endif
|
||||
#ifdef HAVE_FABS
|
||||
LIBM_DD(fabs)
|
||||
#else
|
||||
LIBM_DD_NO(fabs)
|
||||
#endif
|
||||
#ifdef HAVE_FDIM
|
||||
LIBM_DDD(fdim)
|
||||
#else
|
||||
LIBM_DDD_NO(fdim)
|
||||
#endif
|
||||
#ifdef HAVE_FMA
|
||||
LIBM_DDDD(fma)
|
||||
#else
|
||||
LIBM_DDDD_NO(fma)
|
||||
#endif
|
||||
#ifdef HAVE_FMAX
|
||||
LIBM_DDD(fmax)
|
||||
#else
|
||||
LIBM_DDD_NO(fmax)
|
||||
#endif
|
||||
#ifdef HAVE_FMIN
|
||||
LIBM_DDD(fmin)
|
||||
#else
|
||||
LIBM_DDD_NO(fmin)
|
||||
#endif
|
||||
#ifdef HAVE_FMOD
|
||||
LIBM_DDD(fmod)
|
||||
#else
|
||||
LIBM_DDD_NO(fmod)
|
||||
#endif
|
||||
#ifdef HAVE_GAMMA
|
||||
LIBM_DD(gamma)
|
||||
#else
|
||||
LIBM_DD_NO(gamma)
|
||||
#endif
|
||||
#ifdef HAVE_LGAMMA
|
||||
LIBM_DD(lgamma)
|
||||
#else
|
||||
LIBM_DD_NO(lgamma)
|
||||
#endif
|
||||
#ifdef HAVE_LOG1P
|
||||
LIBM_DD(log1p)
|
||||
#else
|
||||
LIBM_DD_NO(log1p)
|
||||
#endif
|
||||
#ifdef HAVE_LOGB
|
||||
LIBM_DD(logb)
|
||||
#else
|
||||
LIBM_DD_NO(logb)
|
||||
#endif
|
||||
#ifdef HAVE_NEARBYINT
|
||||
LIBM_DD(nearbyint)
|
||||
#else
|
||||
LIBM_DD_NO(nearbyint)
|
||||
#endif
|
||||
#ifdef HAVE_NEXTAFTER
|
||||
LIBM_DDD(nextafter)
|
||||
#else
|
||||
LIBM_DDD_NO(nextafter)
|
||||
#endif
|
||||
#ifdef HAVE_NEXTTOWARD
|
||||
LIBM_DDD(nexttoward)
|
||||
#else
|
||||
LIBM_DDD_NO(nexttoward)
|
||||
#endif
|
||||
#if defined(HAVE_POW10) && !defined(WIN32)
|
||||
LIBM_DD(pow10)
|
||||
#else
|
||||
LIBM_DD_NO(pow10)
|
||||
#endif
|
||||
#ifdef HAVE_RINT
|
||||
LIBM_DD(rint)
|
||||
#else
|
||||
LIBM_DD_NO(rint)
|
||||
#endif
|
||||
#ifdef HAVE_ROUND
|
||||
LIBM_DD(round)
|
||||
#else
|
||||
LIBM_DD_NO(round)
|
||||
#endif
|
||||
#ifdef HAVE_SCALB
|
||||
LIBM_DDD(scalb)
|
||||
#else
|
||||
LIBM_DDD_NO(scalb)
|
||||
#endif
|
||||
#ifdef HAVE_SCALBLN
|
||||
LIBM_DDD(scalbln)
|
||||
#else
|
||||
LIBM_DDD_NO(scalbln)
|
||||
#endif
|
||||
#if defined(HAVE_SIGNIFICAND) && !defined(WIN32)
|
||||
LIBM_DD(significand)
|
||||
#else
|
||||
LIBM_DD_NO(significand)
|
||||
#endif
|
||||
#ifdef HAVE_TRUNC
|
||||
LIBM_DD(trunc)
|
||||
#else
|
||||
LIBM_DD_NO(trunc)
|
||||
#endif
|
||||
#ifdef HAVE_LDEXP
|
||||
LIBM_DDD(ldexp)
|
||||
#else
|
||||
LIBM_DDD_NO(ldexp)
|
||||
#endif
|
||||
+5
-2
@@ -19,6 +19,7 @@
|
||||
#include "parser.h"
|
||||
#include "util.h"
|
||||
#include "compile.h"
|
||||
#include "jv_alloc.h"
|
||||
|
||||
struct lib_loading_state {
|
||||
char **names;
|
||||
@@ -278,6 +279,8 @@ static int process_dependencies(jq_state *jq, jv jq_origin, jv lib_origin, block
|
||||
if (nerrors == 0) {
|
||||
// Bind the library to the program
|
||||
bk = block_bind_library(dep_def_block, bk, OP_IS_CALL_PSEUDO, as_str);
|
||||
if (is_data)
|
||||
bk = block_bind_library(dep_def_block, bk, OP_IS_CALL_PSEUDO, NULL);
|
||||
}
|
||||
}
|
||||
jv_free(as);
|
||||
@@ -324,8 +327,8 @@ static int load_library(jq_state *jq, jv lib_path, int is_data, int raw, const c
|
||||
}
|
||||
}
|
||||
state_idx = lib_state->ct++;
|
||||
lib_state->names = realloc(lib_state->names, lib_state->ct * sizeof(const char *));
|
||||
lib_state->defs = realloc(lib_state->defs, lib_state->ct * sizeof(block));
|
||||
lib_state->names = jv_mem_realloc(lib_state->names, lib_state->ct * sizeof(const char *));
|
||||
lib_state->defs = jv_mem_realloc(lib_state->defs, lib_state->ct * sizeof(block));
|
||||
lib_state->names[state_idx] = strdup(jv_string_value(lib_path));
|
||||
lib_state->defs[state_idx] = program;
|
||||
*out_block = program;
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ struct locfile* locfile_init(jq_state *jq, const char *fname, const char* data,
|
||||
for (int i=0; i<length; i++) {
|
||||
if (data[i] == '\n') l->nlines++;
|
||||
}
|
||||
l->linemap = jv_mem_alloc(sizeof(int) * (l->nlines + 1));
|
||||
l->linemap = jv_mem_calloc(sizeof(int), (l->nlines + 1));
|
||||
l->linemap[0] = 0;
|
||||
int line = 1;
|
||||
for (int i=0; i<length; i++) {
|
||||
|
||||
+155
-75
@@ -41,7 +41,7 @@ static const char* progname;
|
||||
* For a longer help message we could use a better option parsing
|
||||
* strategy, one that lets stack options.
|
||||
*/
|
||||
static void usage(int code) {
|
||||
static void usage(int code, int keep_it_short) {
|
||||
FILE *f = stderr;
|
||||
|
||||
if (code == 0)
|
||||
@@ -49,7 +49,9 @@ static void usage(int code) {
|
||||
|
||||
int ret = fprintf(f,
|
||||
"jq - commandline JSON processor [version %s]\n"
|
||||
"\nUsage:\n\n\t%s [options] <jq filter> [file...]\n\n"
|
||||
"\nUsage:\t%s [options] <jq filter> [file...]\n"
|
||||
"\t%s [options] --args <jq filter> [strings...]\n"
|
||||
"\t%s [options] --jsonargs <jq filter> [JSON_TEXTS...]\n\n"
|
||||
"jq is a tool for processing JSON inputs, applying the given filter to\n"
|
||||
"its JSON text inputs and producing the filter's results as JSON on\n"
|
||||
"standard output.\n\n"
|
||||
@@ -59,22 +61,36 @@ static void usage(int code) {
|
||||
"For more advanced filters see the jq(1) manpage (\"man jq\")\n"
|
||||
"and/or https://stedolan.github.io/jq\n\n"
|
||||
"Example:\n\n\t$ echo '{\"foo\": 0}' | jq .\n"
|
||||
"\t{\n\t\t\"foo\": 0\n\t}\n\n"
|
||||
"Some of the options include:\n"
|
||||
" -c compact instead of pretty-printed output;\n"
|
||||
" -n use `null` as the single input value;\n"
|
||||
" -e set the exit status code based on the output;\n"
|
||||
" -s read (slurp) all inputs into an array; apply filter to it;\n"
|
||||
" -r output raw strings, not JSON texts;\n"
|
||||
" -R read raw strings, not JSON texts;\n"
|
||||
" -C colorize JSON;\n"
|
||||
" -M monochrome (don't colorize JSON);\n"
|
||||
" -S sort keys of objects on output;\n"
|
||||
" --tab use tabs for indentation;\n"
|
||||
" --arg a v set variable $a to value <v>;\n"
|
||||
" --argjson a v set variable $a to JSON value <v>;\n"
|
||||
" --slurpfile a f set variable $a to an array of JSON texts read from <f>;\n"
|
||||
"\nSee the manpage for more options.\n", JQ_VERSION, progname);
|
||||
"\t{\n\t\t\"foo\": 0\n\t}\n\n",
|
||||
JQ_VERSION, progname, progname, progname);
|
||||
if (keep_it_short) {
|
||||
fprintf(f,
|
||||
"For a listing of options, use %s --help.\n",
|
||||
progname);
|
||||
} else {
|
||||
(void) fprintf(f,
|
||||
"Some of the options include:\n"
|
||||
" -c compact instead of pretty-printed output;\n"
|
||||
" -n use `null` as the single input value;\n"
|
||||
" -e set the exit status code based on the output;\n"
|
||||
" -s read (slurp) all inputs into an array; apply filter to it;\n"
|
||||
" -r output raw strings, not JSON texts;\n"
|
||||
" -R read raw strings, not JSON texts;\n"
|
||||
" -C colorize JSON;\n"
|
||||
" -M monochrome (don't colorize JSON);\n"
|
||||
" -S sort keys of objects on output;\n"
|
||||
" --tab use tabs for indentation;\n"
|
||||
" --arg a v set variable $a to value <v>;\n"
|
||||
" --argjson a v set variable $a to JSON value <v>;\n"
|
||||
" --slurpfile a f set variable $a to an array of JSON texts read from <f>;\n"
|
||||
" --rawfile a f set variable $a to a string consisting of the contents of <f>;\n"
|
||||
" --args remaining arguments are string arguments, not files;\n"
|
||||
" --jsonargs remaining arguments are JSON arguments, not files;\n"
|
||||
" -- terminates argument processing;\n\n"
|
||||
"Named arguments are also available as $ARGS.named[], while\n"
|
||||
"positional arguments are available as $ARGS.positional[].\n"
|
||||
"\nSee the manpage for more options.\n");
|
||||
}
|
||||
exit((ret < 0 && code == 0) ? 2 : code);
|
||||
}
|
||||
|
||||
@@ -122,10 +138,11 @@ enum {
|
||||
RAW_NO_LF = 1024,
|
||||
UNBUFFERED_OUTPUT = 2048,
|
||||
EXIT_STATUS = 4096,
|
||||
SEQ = 8192,
|
||||
RUN_TESTS = 16384,
|
||||
EXIT_STATUS_EXACT = 8192,
|
||||
SEQ = 16384,
|
||||
RUN_TESTS = 32768,
|
||||
/* debugging only */
|
||||
DUMP_DISASM = 32768,
|
||||
DUMP_DISASM = 65536,
|
||||
};
|
||||
static int options = 0;
|
||||
|
||||
@@ -150,7 +167,11 @@ static int process(jq_state *jq, jv value, int flags, int dumpopts) {
|
||||
jv result;
|
||||
while (jv_is_valid(result = jq_next(jq))) {
|
||||
if ((options & RAW_OUTPUT) && jv_get_kind(result) == JV_KIND_STRING) {
|
||||
fwrite(jv_string_value(result), 1, jv_string_length_bytes(jv_copy(result)), stdout);
|
||||
if (options & ASCII_OUTPUT) {
|
||||
jv_dumpf(result, stdout, JV_PRINT_ASCII);
|
||||
} else {
|
||||
fwrite(jv_string_value(result), 1, jv_string_length_bytes(jv_copy(result)), stdout);
|
||||
}
|
||||
ret = 0;
|
||||
jv_free(result);
|
||||
} else {
|
||||
@@ -167,7 +188,29 @@ static int process(jq_state *jq, jv value, int flags, int dumpopts) {
|
||||
if (options & UNBUFFERED_OUTPUT)
|
||||
fflush(stdout);
|
||||
}
|
||||
if (jv_invalid_has_msg(jv_copy(result))) {
|
||||
if (jq_halted(jq)) {
|
||||
// jq program invoked `halt` or `halt_error`
|
||||
options |= EXIT_STATUS_EXACT;
|
||||
jv exit_code = jq_get_exit_code(jq);
|
||||
if (!jv_is_valid(exit_code))
|
||||
ret = 0;
|
||||
else if (jv_get_kind(exit_code) == JV_KIND_NUMBER)
|
||||
ret = jv_number_value(exit_code);
|
||||
else
|
||||
ret = 5;
|
||||
jv_free(exit_code);
|
||||
jv error_message = jq_get_error_message(jq);
|
||||
if (jv_get_kind(error_message) == JV_KIND_STRING) {
|
||||
fprintf(stderr, "%s", jv_string_value(error_message));
|
||||
} else if (jv_get_kind(error_message) == JV_KIND_NULL) {
|
||||
// Halt with no output
|
||||
} else if (jv_is_valid(error_message)) {
|
||||
error_message = jv_dump_string(jv_copy(error_message), 0);
|
||||
fprintf(stderr, "%s\n", jv_string_value(error_message));
|
||||
} // else no message on stderr; use --debug-trace to see a message
|
||||
fflush(stderr);
|
||||
jv_free(error_message);
|
||||
} else if (jv_invalid_has_msg(jv_copy(result))) {
|
||||
// Uncaught jq exception
|
||||
jv msg = jv_invalid_get_msg(jv_copy(result));
|
||||
jv input_pos = jq_util_input_get_position(jq);
|
||||
@@ -200,10 +243,10 @@ int main(int argc, char* argv[]) {
|
||||
int parser_flags = 0;
|
||||
int nfiles = 0;
|
||||
int badwrite;
|
||||
jv program_arguments = jv_array();
|
||||
jv ARGS = jv_array(); /* positional arguments */
|
||||
jv program_arguments = jv_object(); /* named arguments */
|
||||
|
||||
#ifdef WIN32
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
_setmode(fileno(stdout), _O_TEXT | _O_U8TEXT);
|
||||
@@ -235,21 +278,35 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
jq_util_input_state *input_state = jq_util_input_init(NULL, NULL); // XXX add err_cb
|
||||
|
||||
int further_args_are_files = 0;
|
||||
int further_args_are_strings = 0;
|
||||
int further_args_are_json = 0;
|
||||
int args_done = 0;
|
||||
int jq_flags = 0;
|
||||
size_t short_opts = 0;
|
||||
jv lib_search_paths = jv_null();
|
||||
for (int i=1; i<argc; i++, short_opts = 0) {
|
||||
if (further_args_are_files) {
|
||||
jq_util_input_add_input(input_state, argv[i]);
|
||||
nfiles++;
|
||||
} else if (!strcmp(argv[i], "--")) {
|
||||
if (!program) usage(2);
|
||||
further_args_are_files = 1;
|
||||
} else if (!isoptish(argv[i])) {
|
||||
if (program) {
|
||||
if (args_done) {
|
||||
if (further_args_are_strings) {
|
||||
ARGS = jv_array_append(ARGS, jv_string(argv[i]));
|
||||
} else if (further_args_are_json) {
|
||||
ARGS = jv_array_append(ARGS, jv_parse(argv[i]));
|
||||
} else {
|
||||
jq_util_input_add_input(input_state, argv[i]);
|
||||
nfiles++;
|
||||
}
|
||||
} else if (!strcmp(argv[i], "--")) {
|
||||
if (!program) usage(2, 1);
|
||||
args_done = 1;
|
||||
} else if (!isoptish(argv[i])) {
|
||||
if (program) {
|
||||
if (further_args_are_strings) {
|
||||
ARGS = jv_array_append(ARGS, jv_string(argv[i]));
|
||||
} else if (further_args_are_json) {
|
||||
ARGS = jv_array_append(ARGS, jv_parse(argv[i]));
|
||||
} else {
|
||||
jq_util_input_add_input(input_state, argv[i]);
|
||||
nfiles++;
|
||||
}
|
||||
} else {
|
||||
program = argv[i];
|
||||
}
|
||||
@@ -295,7 +352,7 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
if (isoption(argv[i], 0, "unbuffered", &short_opts)) {
|
||||
options |= UNBUFFERED_OUTPUT;
|
||||
if (!short_opts) continue;
|
||||
continue;
|
||||
}
|
||||
if (isoption(argv[i], 'S', "sort-keys", &short_opts)) {
|
||||
options |= SORTED_OUTPUT;
|
||||
@@ -320,7 +377,7 @@ int main(int argc, char* argv[]) {
|
||||
if (isoption(argv[i], 0, "tab", &short_opts)) {
|
||||
dumpopts &= ~JV_PRINT_INDENT_FLAGS(7);
|
||||
dumpopts |= JV_PRINT_TAB | JV_PRINT_PRETTY;
|
||||
if (!short_opts) continue;
|
||||
continue;
|
||||
}
|
||||
if (isoption(argv[i], 0, "indent", &short_opts)) {
|
||||
if (i >= argc - 1) {
|
||||
@@ -335,95 +392,108 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
dumpopts |= JV_PRINT_INDENT_FLAGS(indent);
|
||||
i++;
|
||||
if (!short_opts) continue;
|
||||
continue;
|
||||
}
|
||||
if (isoption(argv[i], 0, "seq", &short_opts)) {
|
||||
options |= SEQ;
|
||||
if (!short_opts) continue;
|
||||
continue;
|
||||
}
|
||||
if (isoption(argv[i], 0, "stream", &short_opts)) {
|
||||
parser_flags |= JV_PARSE_STREAMING;
|
||||
if (!short_opts) continue;
|
||||
continue;
|
||||
}
|
||||
if (isoption(argv[i], 0, "stream-errors", &short_opts)) {
|
||||
parser_flags |= JV_PARSE_STREAM_ERRORS;
|
||||
if (!short_opts) continue;
|
||||
continue;
|
||||
}
|
||||
if (isoption(argv[i], 'e', "exit-status", &short_opts)) {
|
||||
options |= EXIT_STATUS;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
// FIXME: For --arg* we should check that the varname is acceptable
|
||||
if (isoption(argv[i], 0, "args", &short_opts)) {
|
||||
further_args_are_strings = 1;
|
||||
further_args_are_json = 0;
|
||||
continue;
|
||||
}
|
||||
if (isoption(argv[i], 0, "jsonargs", &short_opts)) {
|
||||
further_args_are_strings = 0;
|
||||
further_args_are_json = 1;
|
||||
continue;
|
||||
}
|
||||
if (isoption(argv[i], 0, "arg", &short_opts)) {
|
||||
if (i >= argc - 2) {
|
||||
fprintf(stderr, "%s: --arg takes two parameters (e.g. --arg varname value)\n", progname);
|
||||
die();
|
||||
}
|
||||
jv arg = jv_object();
|
||||
arg = jv_object_set(arg, jv_string("name"), jv_string(argv[i+1]));
|
||||
arg = jv_object_set(arg, jv_string("value"), jv_string(argv[i+2]));
|
||||
program_arguments = jv_array_append(program_arguments, arg);
|
||||
if (!jv_object_has(jv_copy(program_arguments), jv_string(argv[i+1])))
|
||||
program_arguments = jv_object_set(program_arguments, jv_string(argv[i+1]), jv_string(argv[i+2]));
|
||||
i += 2; // skip the next two arguments
|
||||
if (!short_opts) continue;
|
||||
continue;
|
||||
}
|
||||
if (isoption(argv[i], 0, "argjson", &short_opts)) {
|
||||
if (i >= argc - 2) {
|
||||
fprintf(stderr, "%s: --argjson takes two parameters (e.g. --argjson varname text)\n", progname);
|
||||
die();
|
||||
}
|
||||
jv v = jv_parse(argv[i+2]);
|
||||
if (!jv_is_valid(v)) {
|
||||
fprintf(stderr, "%s: invalid JSON text passed to --argjson\n", progname);
|
||||
die();
|
||||
if (!jv_object_has(jv_copy(program_arguments), jv_string(argv[i+1]))) {
|
||||
jv v = jv_parse(argv[i+2]);
|
||||
if (!jv_is_valid(v)) {
|
||||
fprintf(stderr, "%s: invalid JSON text passed to --argjson\n", progname);
|
||||
die();
|
||||
}
|
||||
program_arguments = jv_object_set(program_arguments, jv_string(argv[i+1]), v);
|
||||
}
|
||||
jv arg = jv_object();
|
||||
arg = jv_object_set(arg, jv_string("name"), jv_string(argv[i+1]));
|
||||
arg = jv_object_set(arg, jv_string("value"), v);
|
||||
program_arguments = jv_array_append(program_arguments, arg);
|
||||
i += 2; // skip the next two arguments
|
||||
if (!short_opts) continue;
|
||||
continue;
|
||||
}
|
||||
if (isoption(argv[i], 0, "argfile", &short_opts) ||
|
||||
isoption(argv[i], 0, "rawfile", &short_opts) ||
|
||||
isoption(argv[i], 0, "slurpfile", &short_opts)) {
|
||||
int raw = isoption(argv[i], 0, "rawfile", &short_opts);
|
||||
const char *which;
|
||||
if (isoption(argv[i], 0, "argfile", &short_opts))
|
||||
which = "argfile";
|
||||
else if (raw)
|
||||
which = "rawfile";
|
||||
else
|
||||
which = "slurpfile";
|
||||
if (i >= argc - 2) {
|
||||
fprintf(stderr, "%s: --%s takes two parameters (e.g. --%s varname filename)\n", progname, which, which);
|
||||
die();
|
||||
}
|
||||
jv arg = jv_object();
|
||||
arg = jv_object_set(arg, jv_string("name"), jv_string(argv[i+1]));
|
||||
jv data = jv_load_file(argv[i+2], 0);
|
||||
if (!jv_is_valid(data)) {
|
||||
data = jv_invalid_get_msg(data);
|
||||
fprintf(stderr, "%s: Bad JSON in --%s %s %s: %s\n", progname, which,
|
||||
argv[i+1], argv[i+2], jv_string_value(data));
|
||||
jv_free(data);
|
||||
jv_free(arg);
|
||||
ret = 2;
|
||||
goto out;
|
||||
if (!jv_object_has(jv_copy(program_arguments), jv_string(argv[i+1]))) {
|
||||
jv data = jv_load_file(argv[i+2], raw);
|
||||
if (!jv_is_valid(data)) {
|
||||
data = jv_invalid_get_msg(data);
|
||||
fprintf(stderr, "%s: Bad JSON in --%s %s %s: %s\n", progname, which,
|
||||
argv[i+1], argv[i+2], jv_string_value(data));
|
||||
jv_free(data);
|
||||
ret = 2;
|
||||
goto out;
|
||||
}
|
||||
if (strcmp(which, "argfile") == 0 &&
|
||||
jv_get_kind(data) == JV_KIND_ARRAY && jv_array_length(jv_copy(data)) == 1)
|
||||
data = jv_array_get(data, 0);
|
||||
program_arguments = jv_object_set(program_arguments, jv_string(argv[i+1]), data);
|
||||
}
|
||||
if (isoption(argv[i], 0, "argfile", &short_opts) &&
|
||||
jv_get_kind(data) == JV_KIND_ARRAY && jv_array_length(jv_copy(data)) == 1)
|
||||
data = jv_array_get(data, 0);
|
||||
arg = jv_object_set(arg, jv_string("value"), data);
|
||||
program_arguments = jv_array_append(program_arguments, arg);
|
||||
i += 2; // skip the next two arguments
|
||||
if (!short_opts) continue;
|
||||
continue;
|
||||
}
|
||||
if (isoption(argv[i], 0, "debug-dump-disasm", &short_opts)) {
|
||||
options |= DUMP_DISASM;
|
||||
continue;
|
||||
}
|
||||
if (isoption(argv[i], 0, "debug-trace=all", &short_opts)) {
|
||||
jq_flags |= JQ_DEBUG_TRACE_ALL;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 0, "debug-trace", &short_opts)) {
|
||||
jq_flags |= JQ_DEBUG_TRACE;
|
||||
if (!short_opts) continue;
|
||||
continue;
|
||||
}
|
||||
if (isoption(argv[i], 'h', "help", &short_opts)) {
|
||||
usage(0);
|
||||
usage(0, 0);
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 'V', "version", &short_opts)) {
|
||||
@@ -464,6 +534,9 @@ int main(int argc, char* argv[]) {
|
||||
if (options & COLOR_OUTPUT) dumpopts |= JV_PRINT_COLOR;
|
||||
if (options & NO_COLOR_OUTPUT) dumpopts &= ~JV_PRINT_COLOR;
|
||||
|
||||
if (getenv("JQ_COLORS") != NULL && !jq_set_colors(getenv("JQ_COLORS")))
|
||||
fprintf(stderr, "Failed to set $JQ_COLORS\n");
|
||||
|
||||
if (jv_get_kind(lib_search_paths) == JV_KIND_NULL) {
|
||||
// Default search path list
|
||||
lib_search_paths = JV_ARRAY(jv_string("~/.jq"),
|
||||
@@ -490,7 +563,7 @@ int main(int argc, char* argv[]) {
|
||||
program = ".";
|
||||
#endif
|
||||
|
||||
if (!program) usage(2);
|
||||
if (!program) usage(2, 1);
|
||||
|
||||
if (options & FROM_FILE) {
|
||||
char *program_origin = strdup(program);
|
||||
@@ -508,11 +581,17 @@ int main(int argc, char* argv[]) {
|
||||
goto out;
|
||||
}
|
||||
jq_set_attr(jq, jv_string("PROGRAM_ORIGIN"), jq_realpath(jv_string(dirname(program_origin))));
|
||||
ARGS = JV_OBJECT(jv_string("positional"), ARGS,
|
||||
jv_string("named"), jv_copy(program_arguments));
|
||||
program_arguments = jv_object_set(program_arguments, jv_string("ARGS"), jv_copy(ARGS));
|
||||
compiled = jq_compile_args(jq, skip_shebang(jv_string_value(data)), jv_copy(program_arguments));
|
||||
free(program_origin);
|
||||
jv_free(data);
|
||||
} else {
|
||||
jq_set_attr(jq, jv_string("PROGRAM_ORIGIN"), jq_realpath(jv_string("."))); // XXX is this good?
|
||||
ARGS = JV_OBJECT(jv_string("positional"), ARGS,
|
||||
jv_string("named"), jv_copy(program_arguments));
|
||||
program_arguments = jv_object_set(program_arguments, jv_string("ARGS"), jv_copy(ARGS));
|
||||
compiled = jq_compile_args(jq, program, jv_copy(program_arguments));
|
||||
}
|
||||
if (!compiled){
|
||||
@@ -577,12 +656,13 @@ out:
|
||||
ret = 2;
|
||||
}
|
||||
|
||||
jv_free(ARGS);
|
||||
jv_free(program_arguments);
|
||||
jq_util_input_free(&input_state);
|
||||
jq_teardown(&jq);
|
||||
if (ret >= 10 && (options & EXIT_STATUS))
|
||||
return ret - 10;
|
||||
if (ret >= 10)
|
||||
if (ret >= 10 && !(options & EXIT_STATUS_EXACT))
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
@@ -2,6 +2,7 @@ OP(LOADK, CONSTANT, 1, 1)
|
||||
OP(DUP, NONE, 1, 2)
|
||||
OP(DUPN, NONE, 1, 2)
|
||||
OP(DUP2, NONE, 2, 3)
|
||||
OP(PUSHK_UNDER, CONSTANT, 1, 2)
|
||||
OP(POP, NONE, 1, 0)
|
||||
OP(LOADV, VARIABLE, 1, 1)
|
||||
OP(LOADVN, VARIABLE, 1, 1)
|
||||
@@ -42,3 +43,6 @@ OP(CLOSURE_PARAM_REGULAR, DEFINITION, 0, 0)
|
||||
OP(DEPS, CONSTANT, 0, 0)
|
||||
OP(MODULEMETA, CONSTANT, 0, 0)
|
||||
OP(GENLABEL, NONE, 0, 1)
|
||||
|
||||
OP(DESTRUCTURE_ALT, BRANCH, 0, 0)
|
||||
OP(STOREVN, VARIABLE, 1, 0)
|
||||
+1068
-989
File diff suppressed because it is too large.
Load diff
+22
-18
@@ -1,8 +1,8 @@
|
||||
/* A Bison parser, made by GNU Bison 3.0.2. */
|
||||
/* A Bison parser, made by GNU Bison 3.0.4. */
|
||||
|
||||
/* Bison interface for Yacc-like parsers in C
|
||||
|
||||
Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc.
|
||||
Copyright (C) 1984, 1989-1990, 2000-2015 Free Software Foundation, Inc.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -101,13 +101,14 @@ struct lexer_param;
|
||||
SETDEFINEDOR = 292,
|
||||
LESSEQ = 293,
|
||||
GREATEREQ = 294,
|
||||
QQSTRING_START = 295,
|
||||
QQSTRING_TEXT = 296,
|
||||
QQSTRING_INTERP_START = 297,
|
||||
QQSTRING_INTERP_END = 298,
|
||||
QQSTRING_END = 299,
|
||||
FUNCDEF = 300,
|
||||
NONOPT = 301
|
||||
ALTERNATION = 295,
|
||||
QQSTRING_START = 296,
|
||||
QQSTRING_TEXT = 297,
|
||||
QQSTRING_INTERP_START = 298,
|
||||
QQSTRING_INTERP_END = 299,
|
||||
QQSTRING_END = 300,
|
||||
FUNCDEF = 301,
|
||||
NONOPT = 302
|
||||
};
|
||||
#endif
|
||||
/* Tokens. */
|
||||
@@ -148,17 +149,18 @@ struct lexer_param;
|
||||
#define SETDEFINEDOR 292
|
||||
#define LESSEQ 293
|
||||
#define GREATEREQ 294
|
||||
#define QQSTRING_START 295
|
||||
#define QQSTRING_TEXT 296
|
||||
#define QQSTRING_INTERP_START 297
|
||||
#define QQSTRING_INTERP_END 298
|
||||
#define QQSTRING_END 299
|
||||
#define FUNCDEF 300
|
||||
#define NONOPT 301
|
||||
#define ALTERNATION 295
|
||||
#define QQSTRING_START 296
|
||||
#define QQSTRING_TEXT 297
|
||||
#define QQSTRING_INTERP_START 298
|
||||
#define QQSTRING_INTERP_END 299
|
||||
#define QQSTRING_END 300
|
||||
#define FUNCDEF 301
|
||||
#define NONOPT 302
|
||||
|
||||
/* Value type. */
|
||||
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
|
||||
typedef union YYSTYPE YYSTYPE;
|
||||
|
||||
union YYSTYPE
|
||||
{
|
||||
#line 31 "src/parser.y" /* yacc.c:1909 */
|
||||
@@ -166,8 +168,10 @@ union YYSTYPE
|
||||
jv literal;
|
||||
block blk;
|
||||
|
||||
#line 170 "src/parser.h" /* yacc.c:1909 */
|
||||
#line 172 "src/parser.h" /* yacc.c:1909 */
|
||||
};
|
||||
|
||||
typedef union YYSTYPE YYSTYPE;
|
||||
# define YYSTYPE_IS_TRIVIAL 1
|
||||
# define YYSTYPE_IS_DECLARED 1
|
||||
#endif
|
||||
|
||||
+25
-10
@@ -83,6 +83,7 @@ struct lexer_param;
|
||||
%token SETDEFINEDOR "//="
|
||||
%token LESSEQ "<="
|
||||
%token GREATEREQ ">="
|
||||
%token ALTERNATION "?//"
|
||||
|
||||
%token QQSTRING_START
|
||||
%token <literal> QQSTRING_TEXT
|
||||
@@ -118,7 +119,7 @@ struct lexer_param;
|
||||
%type <blk> FuncDef FuncDefs
|
||||
%type <blk> Module Import Imports ImportWhat ImportFrom
|
||||
%type <blk> Param Params Arg Args
|
||||
%type <blk> Pattern ArrayPats ObjPats ObjPat
|
||||
%type <blk> Patterns RepPatterns Pattern ArrayPats ObjPats ObjPat
|
||||
%type <literal> Keyword
|
||||
%{
|
||||
#include "lexer.h"
|
||||
@@ -176,10 +177,6 @@ static jv check_object_key(block k) {
|
||||
return jv_invalid();
|
||||
}
|
||||
|
||||
static block gen_dictpair(block k, block v) {
|
||||
return BLOCK(gen_subexp(k), gen_subexp(v), gen_op_simple(INSERT));
|
||||
}
|
||||
|
||||
static block gen_index(block obj, block key) {
|
||||
return BLOCK(gen_subexp(key), obj, gen_op_simple(INDEX));
|
||||
}
|
||||
@@ -342,19 +339,18 @@ FuncDef Exp %prec FUNCDEF {
|
||||
$$ = block_bind_referenced($1, $2, OP_IS_CALL_PSEUDO);
|
||||
} |
|
||||
|
||||
Term "as" Pattern '|' Exp {
|
||||
Term "as" Patterns '|' Exp {
|
||||
$$ = gen_destructure($1, $3, $5);
|
||||
} |
|
||||
|
||||
"reduce" Term "as" Pattern '(' Exp ';' Exp ')' {
|
||||
"reduce" Term "as" Patterns '(' Exp ';' Exp ')' {
|
||||
$$ = gen_reduce($2, $4, $6, $8);
|
||||
} |
|
||||
|
||||
"foreach" Term "as" Pattern '(' Exp ';' Exp ';' Exp ')' {
|
||||
"foreach" Term "as" Patterns '(' Exp ';' Exp ';' Exp ')' {
|
||||
$$ = gen_foreach($2, $4, $6, $8, $10);
|
||||
} |
|
||||
|
||||
"foreach" Term "as" Pattern '(' Exp ';' Exp ')' {
|
||||
"foreach" Term "as" Patterns '(' Exp ';' Exp ')' {
|
||||
$$ = gen_foreach($2, $4, $6, $8, gen_noop());
|
||||
} |
|
||||
|
||||
@@ -778,6 +774,22 @@ Exp {
|
||||
$$ = gen_lambda($1);
|
||||
}
|
||||
|
||||
RepPatterns:
|
||||
RepPatterns "?//" Pattern {
|
||||
$$ = BLOCK($1, gen_destructure_alt($3));
|
||||
} |
|
||||
Pattern {
|
||||
$$ = gen_destructure_alt($1);
|
||||
}
|
||||
|
||||
Patterns:
|
||||
RepPatterns "?//" Pattern {
|
||||
$$ = BLOCK($1, $3);
|
||||
} |
|
||||
Pattern {
|
||||
$$ = $1;
|
||||
}
|
||||
|
||||
Pattern:
|
||||
'$' IDENT {
|
||||
$$ = gen_op_unbound(STOREV, jv_string_value($2));
|
||||
@@ -810,6 +822,9 @@ ObjPat:
|
||||
'$' IDENT {
|
||||
$$ = gen_object_matcher(gen_const($2), gen_op_unbound(STOREV, jv_string_value($2)));
|
||||
} |
|
||||
'$' IDENT ':' Pattern {
|
||||
$$ = gen_object_matcher(gen_const($2), BLOCK(gen_op_simple(DUP), gen_op_unbound(STOREV, jv_string_value($2)), $4));
|
||||
} |
|
||||
IDENT ':' Pattern {
|
||||
$$ = gen_object_matcher(gen_const($1), $3);
|
||||
} |
|
||||
|
||||
+8
-3
@@ -52,7 +52,7 @@ FILE *fopen(const char *fname, const char *mode) {
|
||||
MultiByteToWideChar(CP_UTF8, 0, fname, -1, wfname, sz);
|
||||
|
||||
sz = sizeof(wchar_t) * MultiByteToWideChar(CP_UTF8, 0, mode, -1, NULL, 0);
|
||||
wchar_t *wmode = alloca(sz); // +2 is not needed, but just in case
|
||||
wchar_t *wmode = alloca(sz + 2); // +2 is not needed, but just in case
|
||||
MultiByteToWideChar(CP_UTF8, 0, mode, -1, wmode, sz);
|
||||
return _wfopen(wfname, wmode);
|
||||
}
|
||||
@@ -139,7 +139,7 @@ jv jq_realpath(jv path) {
|
||||
path_max = PATH_MAX;
|
||||
#endif
|
||||
if (path_max > 0) {
|
||||
buf = malloc(sizeof(char) * path_max);
|
||||
buf = jv_mem_alloc(path_max);
|
||||
}
|
||||
#ifdef WIN32
|
||||
char *tmp = _fullpath(buf, jv_string_value(path), path_max);
|
||||
@@ -410,6 +410,7 @@ jv jq_util_input_get_current_line(jq_state* jq) {
|
||||
// When slurping, it returns just one value
|
||||
jv jq_util_input_next_input(jq_util_input_state *state) {
|
||||
int is_last = 0;
|
||||
int has_more = 0;
|
||||
jv value = jv_invalid(); // need more input
|
||||
do {
|
||||
if (state->parser == NULL) {
|
||||
@@ -441,6 +442,10 @@ jv jq_util_input_next_input(jq_util_input_state *state) {
|
||||
}
|
||||
value = jv_parser_next(state->parser);
|
||||
if (jv_is_valid(state->slurped)) {
|
||||
// When slurping an input that doesn't have a trailing newline,
|
||||
// we might have more than one value on the same line, so let's check
|
||||
// to see if we have more data to parse.
|
||||
has_more = jv_parser_remaining(state->parser);
|
||||
if (jv_is_valid(value)) {
|
||||
state->slurped = jv_array_append(state->slurped, value);
|
||||
value = jv_invalid();
|
||||
@@ -450,7 +455,7 @@ jv jq_util_input_next_input(jq_util_input_state *state) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
} while (!is_last);
|
||||
} while (!is_last || has_more);
|
||||
|
||||
if (jv_is_valid(state->slurped)) {
|
||||
value = state->slurped;
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
# Tests are groups of three lines: program, input, expected output
|
||||
# Blank lines and lines starting with # are ignored
|
||||
|
||||
@base64
|
||||
"<>&'\"\t"
|
||||
"PD4mJyIJ"
|
||||
|
||||
# decoding encoded output results in same text
|
||||
(@base64|@base64d)
|
||||
"<>&'\"\t"
|
||||
"<>&'\"\t"
|
||||
|
||||
# regression test for #436
|
||||
@base64
|
||||
"foóbar\n"
|
||||
"Zm/Ds2Jhcgo="
|
||||
|
||||
@base64d
|
||||
"Zm/Ds2Jhcgo="
|
||||
"foóbar\n"
|
||||
|
||||
# optional trailing equals padding (With padding, this is cWl4YmF6Cg==)
|
||||
@base64d
|
||||
"cWl4YmF6Cg"
|
||||
"qixbaz\n"
|
||||
|
||||
# invalid base64 characters (whitespace)
|
||||
. | try @base64d catch .
|
||||
"Not base64 data"
|
||||
"string (\"Not base64...) is not valid base64 data"
|
||||
|
||||
# invalid base64 (too many bytes, QUJD = "ABCD"
|
||||
. | try @base64d catch .
|
||||
"QUJDa"
|
||||
"string (\"QUJDa\") trailing base64 byte found"
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
. "${0%/*}/setup" "$@"
|
||||
|
||||
$VALGRIND $Q $JQ -L "$mods" --run-tests $JQTESTDIR/base64.test
|
||||
+213
-3
@@ -61,7 +61,7 @@ null
|
||||
null
|
||||
"interpolation"
|
||||
|
||||
@text,@json,([1,.] | (@csv, @tsv)),@html,@uri,@sh,@base64
|
||||
@text,@json,([1,.] | (@csv, @tsv)),@html,@uri,@sh,@base64,(@base64 | @base64d)
|
||||
"<>&'\"\t"
|
||||
"<>&'\"\t"
|
||||
"\"<>&'\\\"\\t\""
|
||||
@@ -71,12 +71,17 @@ null
|
||||
"%3C%3E%26'%22%09"
|
||||
"'<>&'\\''\"\t'"
|
||||
"PD4mJyIJ"
|
||||
"<>&'\"\t"
|
||||
|
||||
# regression test for #436
|
||||
@base64
|
||||
"foóbar\n"
|
||||
"Zm/Ds2Jhcgo="
|
||||
|
||||
@base64d
|
||||
"Zm/Ds2Jhcgo="
|
||||
"foóbar\n"
|
||||
|
||||
@uri
|
||||
"\u03bc"
|
||||
"%CE%BC"
|
||||
@@ -273,7 +278,7 @@ null
|
||||
|
||||
%%FAIL
|
||||
. as $foo | break $foo
|
||||
jq: error: *label-foo/0 is not defined at <top-level>, line 1:
|
||||
jq: error: $*label-foo is not defined at <top-level>, line 1:
|
||||
|
||||
[.[]|[.,1]|until(.[0] < 1; [.[0] - 1, .[1] * .[0]])|.[1]]
|
||||
[1,2,3,4,5]
|
||||
@@ -303,6 +308,11 @@ null
|
||||
10
|
||||
[0,9,0,5,"nth doesn't support negative indices"]
|
||||
|
||||
# Check that first(g) does not extract more than one value from g
|
||||
first(1,error("foo"))
|
||||
null
|
||||
1
|
||||
|
||||
#
|
||||
# Check that various builtins evalute all arguments where appropriate,
|
||||
# doing cartesian products where appropriate.
|
||||
@@ -373,6 +383,15 @@ del(.[2:4],.[0],.[-2:])
|
||||
[0,1,"a","b",4,5,6,7]
|
||||
[0,1,"a","b","c",4,5,6,7]
|
||||
|
||||
# Slices at large offsets (issue #1108)
|
||||
#
|
||||
# This is written this way because [range(<large number>)] is
|
||||
# significantly slower under valgrind than .[<large number>] = value.
|
||||
#
|
||||
# We range down rather than up so that we have just one realloc.
|
||||
reduce range(65540;65536;-1) as $i ([]; .[$i] = $i)|.[65536:]
|
||||
null
|
||||
[null,65537,65538,65539,65540]
|
||||
|
||||
#
|
||||
# Variables
|
||||
@@ -686,6 +705,128 @@ reduce [[1,2,10], [3,4,10]][] as [$i,$j] (0; . + $i * $j)
|
||||
null
|
||||
14
|
||||
|
||||
# This, while useless, should still compile.
|
||||
reduce . as $n (.; .)
|
||||
null
|
||||
null
|
||||
|
||||
# Destructuring
|
||||
. as {$a, b: [$c, {$d}]} | [$a, $c, $d]
|
||||
{"a":1, "b":[2,{"d":3}]}
|
||||
[1,2,3]
|
||||
|
||||
. as {$a, $b:[$c, $d]}| [$a, $b, $c, $d]
|
||||
{"a":1, "b":[2,{"d":3}]}
|
||||
[1,[2,{"d":3}],2,{"d":3}]
|
||||
|
||||
# Destructuring with alternation
|
||||
.[] | . as {$a, b: [$c, {$d}]} ?// [$a, {$b}, $e] ?// $f | [$a, $b, $c, $d, $e, $f]
|
||||
[{"a":1, "b":[2,{"d":3}]}, [4, {"b":5, "c":6}, 7, 8, 9], "foo"]
|
||||
[1, null, 2, 3, null, null]
|
||||
[4, 5, null, null, 7, null]
|
||||
[null, null, null, null, null, "foo"]
|
||||
|
||||
# Destructuring DUP/POP issues
|
||||
.[] | . as {a:$a} ?// {a:$a} ?// {a:$a} | $a
|
||||
[[3],[4],[5],6]
|
||||
# Runtime error: "jq: Cannot index array with string \"c\""
|
||||
|
||||
.[] as {a:$a} ?// {a:$a} ?// {a:$a} | $a
|
||||
[[3],[4],[5],6]
|
||||
# Runtime error: "jq: Cannot index array with string \"c\""
|
||||
|
||||
[[3],[4],[5],6][] | . as {a:$a} ?// {a:$a} ?// {a:$a} | $a
|
||||
null
|
||||
# Runtime error: "jq: Cannot index array with string \"c\""
|
||||
|
||||
[[3],[4],[5],6] | .[] as {a:$a} ?// {a:$a} ?// {a:$a} | $a
|
||||
null
|
||||
# Runtime error: "jq: Cannot index array with string \"c\""
|
||||
|
||||
.[] | . as {a:$a} ?// {a:$a} ?// $a | $a
|
||||
[[3],[4],[5],6]
|
||||
[3]
|
||||
[4]
|
||||
[5]
|
||||
6
|
||||
|
||||
.[] as {a:$a} ?// {a:$a} ?// $a | $a
|
||||
[[3],[4],[5],6]
|
||||
[3]
|
||||
[4]
|
||||
[5]
|
||||
6
|
||||
|
||||
[[3],[4],[5],6][] | . as {a:$a} ?// {a:$a} ?// $a | $a
|
||||
null
|
||||
[3]
|
||||
[4]
|
||||
[5]
|
||||
6
|
||||
|
||||
[[3],[4],[5],6] | .[] as {a:$a} ?// {a:$a} ?// $a | $a
|
||||
null
|
||||
[3]
|
||||
[4]
|
||||
[5]
|
||||
6
|
||||
|
||||
.[] | . as {a:$a} ?// $a ?// {a:$a} | $a
|
||||
[[3],[4],[5],6]
|
||||
[3]
|
||||
[4]
|
||||
[5]
|
||||
6
|
||||
|
||||
.[] as {a:$a} ?// $a ?// {a:$a} | $a
|
||||
[[3],[4],[5],6]
|
||||
[3]
|
||||
[4]
|
||||
[5]
|
||||
6
|
||||
|
||||
[[3],[4],[5],6][] | . as {a:$a} ?// $a ?// {a:$a} | $a
|
||||
null
|
||||
[3]
|
||||
[4]
|
||||
[5]
|
||||
6
|
||||
|
||||
[[3],[4],[5],6] | .[] as {a:$a} ?// $a ?// {a:$a} | $a
|
||||
null
|
||||
[3]
|
||||
[4]
|
||||
[5]
|
||||
6
|
||||
|
||||
.[] | . as $a ?// {a:$a} ?// {a:$a} | $a
|
||||
[[3],[4],[5],6]
|
||||
[3]
|
||||
[4]
|
||||
[5]
|
||||
6
|
||||
|
||||
.[] as $a ?// {a:$a} ?// {a:$a} | $a
|
||||
[[3],[4],[5],6]
|
||||
[3]
|
||||
[4]
|
||||
[5]
|
||||
6
|
||||
|
||||
[[3],[4],[5],6][] | . as $a ?// {a:$a} ?// {a:$a} | $a
|
||||
null
|
||||
[3]
|
||||
[4]
|
||||
[5]
|
||||
6
|
||||
|
||||
[[3],[4],[5],6] | .[] as $a ?// {a:$a} ?// {a:$a} | $a
|
||||
null
|
||||
[3]
|
||||
[4]
|
||||
[5]
|
||||
6
|
||||
|
||||
. as $dot|any($dot[];not)
|
||||
[1,2,3,4,true,false,1,2,3,4,5]
|
||||
true
|
||||
@@ -827,6 +968,18 @@ def inc(x): x |= .+1; inc(.[].a)
|
||||
[{"a":1,"b":2},{"a":2,"b":4},{"a":7,"b":8}]
|
||||
[{"a":2,"b":2},{"a":3,"b":4},{"a":8,"b":8}]
|
||||
|
||||
# #1358, getpath/1 should work in path expressions
|
||||
.[] | try (getpath(["a",0,"b"]) |= 5) catch .
|
||||
[null,{"b":0},{"a":0},{"a":null},{"a":[0,1]},{"a":{"b":1}},{"a":[{}]},{"a":[{"c":3}]}]
|
||||
{"a":[{"b":5}]}
|
||||
{"b":0,"a":[{"b":5}]}
|
||||
"Cannot index number with number"
|
||||
{"a":[{"b":5}]}
|
||||
"Cannot index number with string \"b\""
|
||||
"Cannot index object with number"
|
||||
{"a":[{"b":5}]}
|
||||
{"a":[{"c":3,"b":5}]}
|
||||
|
||||
.[2][3] = 1
|
||||
[4]
|
||||
[4, null, [null, null, null, 1]]
|
||||
@@ -1044,7 +1197,13 @@ sort
|
||||
[42,[2,5,3,11],10,{"a":42,"b":2},{"a":42},true,2,[2,6],"hello",null,[2,5,6],{"a":[],"b":1},"abc","ab",[3,10],{},false,"abcd",null]
|
||||
[null,null,false,true,2,10,42,"ab","abc","abcd","hello",[2,5,3,11],[2,5,6],[2,6],[3,10],{},{"a":42},{"a":42,"b":2},{"a":[],"b":1}]
|
||||
|
||||
# Tests of `sort_by` and `group_by` have been moved to tests/sort.test
|
||||
(sort_by(.b) | sort_by(.a)), sort_by(.a, .b), sort_by(.b, .c), group_by(.b), group_by(.a + .b - .c == 2)
|
||||
[{"a": 1, "b": 4, "c": 14}, {"a": 4, "b": 1, "c": 3}, {"a": 1, "b": 4, "c": 3}, {"a": 0, "b": 2, "c": 43}]
|
||||
[{"a": 0, "b": 2, "c": 43}, {"a": 1, "b": 4, "c": 14}, {"a": 1, "b": 4, "c": 3}, {"a": 4, "b": 1, "c": 3}]
|
||||
[{"a": 0, "b": 2, "c": 43}, {"a": 1, "b": 4, "c": 14}, {"a": 1, "b": 4, "c": 3}, {"a": 4, "b": 1, "c": 3}]
|
||||
[{"a": 4, "b": 1, "c": 3}, {"a": 0, "b": 2, "c": 43}, {"a": 1, "b": 4, "c": 3}, {"a": 1, "b": 4, "c": 14}]
|
||||
[[{"a": 4, "b": 1, "c": 3}], [{"a": 0, "b": 2, "c": 43}], [{"a": 1, "b": 4, "c": 14}, {"a": 1, "b": 4, "c": 3}]]
|
||||
[[{"a": 1, "b": 4, "c": 14}, {"a": 0, "b": 2, "c": 43}], [{"a": 4, "b": 1, "c": 3}, {"a": 1, "b": 4, "c": 3}]]
|
||||
|
||||
unique
|
||||
[1,2,5,3,5,3,1,3]
|
||||
@@ -1306,4 +1465,55 @@ jq: error: syntax error, unexpected INVALID_CHARACTER, expecting $end (Unix shel
|
||||
(.[{}] = 0)?
|
||||
null
|
||||
|
||||
INDEX(range(5)|[., "foo\(.)"]; .[0])
|
||||
null
|
||||
{"0":[0,"foo0"],"1":[1,"foo1"],"2":[2,"foo2"],"3":[3,"foo3"],"4":[4,"foo4"]}
|
||||
|
||||
JOIN({"0":[0,"abc"],"1":[1,"bcd"],"2":[2,"def"],"3":[3,"efg"],"4":[4,"fgh"]}; .[0]|tostring)
|
||||
[[5,"foo"],[3,"bar"],[1,"foobar"]]
|
||||
[[[5,"foo"],null],[[3,"bar"],[3,"efg"]],[[1,"foobar"],[1,"bcd"]]]
|
||||
|
||||
range(5;10)|IN(range(10))
|
||||
null
|
||||
true
|
||||
true
|
||||
true
|
||||
true
|
||||
true
|
||||
|
||||
range(10;12)|IN(range(10))
|
||||
null
|
||||
false
|
||||
false
|
||||
|
||||
IN(range(10;20); range(10))
|
||||
null
|
||||
false
|
||||
|
||||
IN(range(5;20); range(10))
|
||||
null
|
||||
true
|
||||
|
||||
# Regression test for #1347
|
||||
(.a as $x | .b) = "b"
|
||||
{"a":null,"b":null}
|
||||
{"a":null,"b":"b"}
|
||||
|
||||
# Regression test for #1368
|
||||
(.. | select(type == "object" and has("b") and (.b | type) == "array")|.b) |= .[0]
|
||||
{"a": {"b": [1, {"b": 3}]}}
|
||||
{"a": {"b": 1}}
|
||||
|
||||
isempty(empty)
|
||||
null
|
||||
true
|
||||
|
||||
isempty(range(3))
|
||||
null
|
||||
false
|
||||
|
||||
isempty(1,error("foo"))
|
||||
null
|
||||
false
|
||||
|
||||
|
||||
+7
-11
@@ -1,21 +1,17 @@
|
||||
# See tests/jq.test and the jq manual for more information.
|
||||
|
||||
# As long as we use the C qsort() function, this test can fail if the C
|
||||
# qsort() implementation we get is not stable (Quicksort is not stable).
|
||||
|
||||
(sort_by(.b) | sort_by(.a)), sort_by(.a, .b), sort_by(.b, .c), group_by(.b), group_by(.a + .b - .c == 2)
|
||||
[{"a": 1, "b": 4, "c": 14}, {"a": 4, "b": 1, "c": 3}, {"a": 1, "b": 4, "c": 3}, {"a": 0, "b": 2, "c": 43}]
|
||||
[{"a": 0, "b": 2, "c": 43}, {"a": 1, "b": 4, "c": 14}, {"a": 1, "b": 4, "c": 3}, {"a": 4, "b": 1, "c": 3}]
|
||||
[{"a": 0, "b": 2, "c": 43}, {"a": 1, "b": 4, "c": 14}, {"a": 1, "b": 4, "c": 3}, {"a": 4, "b": 1, "c": 3}]
|
||||
[{"a": 4, "b": 1, "c": 3}, {"a": 0, "b": 2, "c": 43}, {"a": 1, "b": 4, "c": 3}, {"a": 1, "b": 4, "c": 14}]
|
||||
[[{"a": 4, "b": 1, "c": 3}], [{"a": 0, "b": 2, "c": 43}], [{"a": 1, "b": 4, "c": 14}, {"a": 1, "b": 4, "c": 3}]]
|
||||
[[{"a": 1, "b": 4, "c": 14}, {"a": 0, "b": 2, "c": 43}], [{"a": 4, "b": 1, "c": 3}, {"a": 1, "b": 4, "c": 3}]]
|
||||
|
||||
# strptime() is not available on mingw/WIN32
|
||||
[strptime("%Y-%m-%dT%H:%M:%SZ")|(.,mktime)]
|
||||
"2015-03-05T23:51:47Z"
|
||||
[[2015,2,5,23,51,47,4,63],1425599507]
|
||||
|
||||
# Check day-of-week and day of year computations
|
||||
# (should trip an assert if this fails)
|
||||
# This date range
|
||||
last(range(365 * 67)|("1970-03-01T01:02:03Z"|strptime("%Y-%m-%dT%H:%M:%SZ")|mktime) + (86400 * .)|strftime("%Y-%m-%dT%H:%M:%SZ")|strptime("%Y-%m-%dT%H:%M:%SZ"))
|
||||
null
|
||||
[2037,1,11,1,2,3,3,41]
|
||||
|
||||
# %e is not available on mingw/WIN32
|
||||
strftime("%A, %B %e, %Y")
|
||||
1435677542.822351
|
||||
|
||||
+3
-1
@@ -15,9 +15,11 @@ JQ=$JQBASEDIR/jq
|
||||
if [ -z "${NO_VALGRIND-}" ] && which valgrind > /dev/null; then
|
||||
VALGRIND="valgrind --error-exitcode=1 --leak-check=full \
|
||||
--suppressions=$JQTESTDIR/onig.supp"
|
||||
VG_EXIT0=--error-exitcode=0
|
||||
Q=-q
|
||||
else
|
||||
VALGRIND=
|
||||
VG_EXIT0=
|
||||
Q=
|
||||
fi
|
||||
|
||||
@@ -33,7 +35,7 @@ clean () {
|
||||
fi
|
||||
}
|
||||
trap clean EXIT
|
||||
d=`mktemp -d -t jqXXXXXX || true`
|
||||
d=$(mktemp -d -t jqXXXXXX || true)
|
||||
if [ -z "$d" ]; then
|
||||
echo "Your OS does not support mktemp(1) -d" 1>&2
|
||||
exit 1
|
||||
|
||||
+118
-27
@@ -29,50 +29,46 @@ $VALGRIND $Q $JQ -Rne '[inputs] == ["a\u0000b", "c\u0000d", "e"]' $d/input
|
||||
## move all of these into tests/all.test
|
||||
|
||||
# String constant folding (addition only)
|
||||
n=`$VALGRIND $Q $JQ -n --debug-dump-disasm '"foo"' | wc -l`
|
||||
if [ $n -ne 5 ]; then
|
||||
echo "Constant expression folding for strings didn't work"
|
||||
exit 1
|
||||
fi
|
||||
nref=$($VALGRIND $Q $JQ -n --debug-dump-disasm '"foo"' | wc -l)
|
||||
|
||||
# Numeric constant folding (not all ops yet)
|
||||
n=`$VALGRIND $Q $JQ -n --debug-dump-disasm '1+1' | wc -l`
|
||||
if [ $n -ne 5 ]; then
|
||||
n=$($VALGRIND $Q $JQ -n --debug-dump-disasm '1+1' | wc -l)
|
||||
if [ $n -ne $nref ]; then
|
||||
echo "Constant expression folding for strings didn't work"
|
||||
exit 1
|
||||
fi
|
||||
n=`$VALGRIND $Q $JQ -n --debug-dump-disasm '1-1' | wc -l`
|
||||
if [ $n -ne 5 ]; then
|
||||
n=$($VALGRIND $Q $JQ -n --debug-dump-disasm '1-1' | wc -l)
|
||||
if [ $n -ne $nref ]; then
|
||||
echo "Constant expression folding for strings didn't work"
|
||||
exit 1
|
||||
fi
|
||||
n=`$VALGRIND $Q $JQ -n --debug-dump-disasm '2*3' | wc -l`
|
||||
if [ $n -ne 5 ]; then
|
||||
n=$($VALGRIND $Q $JQ -n --debug-dump-disasm '2*3' | wc -l)
|
||||
if [ $n -ne $nref ]; then
|
||||
echo "Constant expression folding for strings didn't work"
|
||||
exit 1
|
||||
fi
|
||||
n=`$VALGRIND $Q $JQ -n --debug-dump-disasm '9/3' | wc -l`
|
||||
if [ $n -ne 5 ]; then
|
||||
n=$($VALGRIND $Q $JQ -n --debug-dump-disasm '9/3' | wc -l)
|
||||
if [ $n -ne $nref ]; then
|
||||
echo "Constant expression folding for strings didn't work"
|
||||
exit 1
|
||||
fi
|
||||
n=`$VALGRIND $Q $JQ -n --debug-dump-disasm '9==3' | wc -l`
|
||||
if [ $n -ne 5 ]; then
|
||||
n=$($VALGRIND $Q $JQ -n --debug-dump-disasm '9==3' | wc -l)
|
||||
if [ $n -ne $nref ]; then
|
||||
echo "Constant expression folding for strings didn't work"
|
||||
exit 1
|
||||
fi
|
||||
n=`$VALGRIND $Q $JQ -n --debug-dump-disasm '9!=3' | wc -l`
|
||||
if [ $n -ne 5 ]; then
|
||||
n=$($VALGRIND $Q $JQ -n --debug-dump-disasm '9!=3' | wc -l)
|
||||
if [ $n -ne $nref ]; then
|
||||
echo "Constant expression folding for strings didn't work"
|
||||
exit 1
|
||||
fi
|
||||
n=`$VALGRIND $Q $JQ -n --debug-dump-disasm '9<=3' | wc -l`
|
||||
if [ $n -ne 5 ]; then
|
||||
n=$($VALGRIND $Q $JQ -n --debug-dump-disasm '9<=3' | wc -l)
|
||||
if [ $n -ne $nref ]; then
|
||||
echo "Constant expression folding for strings didn't work"
|
||||
exit 1
|
||||
fi
|
||||
n=`$VALGRIND $Q $JQ -n --debug-dump-disasm '9>=3' | wc -l`
|
||||
if [ $n -ne 5 ]; then
|
||||
n=$($VALGRIND $Q $JQ -n --debug-dump-disasm '9>=3' | wc -l)
|
||||
if [ $n -ne $nref ]; then
|
||||
echo "Constant expression folding for strings didn't work"
|
||||
exit 1
|
||||
fi
|
||||
@@ -126,6 +122,14 @@ if $VALGRIND $Q $JQ -e . $d/input; then
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Regression test for #1534
|
||||
echo "[1,2,3,4]" > $d/expected
|
||||
printf "[1,2][3,4]" | $JQ -cs add > $d/out 2>&1
|
||||
cmp $d/out $d/expected
|
||||
printf "[1,2][3,4]\n" | $JQ -cs add > $d/out 2>&1
|
||||
cmp $d/out $d/expected
|
||||
|
||||
|
||||
## Test streaming parser
|
||||
|
||||
## If we add an option to stream to the `import ... as $symbol;` directive
|
||||
@@ -141,15 +145,15 @@ if which seq > /dev/null 2>&1; then
|
||||
# test very, very slow when run with valgrind, and the whole point
|
||||
# is to run it with valgrind.
|
||||
#
|
||||
#len=`wc -c < "$JQTESTDIR/torture/input0.json"`
|
||||
#len=$(wc -c < "$JQTESTDIR/torture/input0.json")
|
||||
if [ -z "$VALGRIND" ]; then
|
||||
start=1
|
||||
end=`wc -c < "$JQTESTDIR/torture/input0.json"`
|
||||
end=$(wc -c < "$JQTESTDIR/torture/input0.json")
|
||||
else
|
||||
start=120
|
||||
end=151
|
||||
fi
|
||||
for i in `seq $start $end`; do
|
||||
for i in $(seq $start $end); do
|
||||
dd "if=tests/torture/input0.json" bs=$i count=1 2>/dev/null |
|
||||
$VALGRIND $JQ -c . > $d/out0 2>$d/err || true
|
||||
if [ -n "$VALGRIND" ]; then
|
||||
@@ -174,7 +178,7 @@ fi
|
||||
|
||||
## Fuzz parser
|
||||
|
||||
## XXX With a `urandom` builtin we could move this test into tests/all.test
|
||||
## XXX With a $(urandom) builtin we could move this test into tests/all.test
|
||||
clean=false
|
||||
if dd if=/dev/urandom bs=16 count=1024 > $d/rand 2>/dev/null; then
|
||||
# Have a /dev/urandom, good
|
||||
@@ -193,12 +197,12 @@ clean=true
|
||||
|
||||
# Check handling of ~/.jq; these can't move into jq_test.c yet because
|
||||
# they depend on $HOME
|
||||
if [ "`HOME="$mods" $VALGRIND $Q $JQ -nr fg`" != foobar ]; then
|
||||
if [ "$(HOME="$mods" $VALGRIND $Q $JQ -nr fg)" != foobar ]; then
|
||||
echo "Bug #479 appears to be back" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ `HOME="$mods" $VALGRIND $Q $JQ --debug-dump-disasm -n fg | grep '^[a-z]' | wc -l` -gt 3 ]; then
|
||||
if [ $(HOME="$mods" $VALGRIND $Q $JQ --debug-dump-disasm -n fg | grep '^[a-z]' | wc -l) -gt 3 ]; then
|
||||
echo "Binding too many defs into program" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
@@ -215,4 +219,91 @@ if ! $VALGRIND $Q $JQ -L tests/modules -ne 'import "test_bind_order" as check; c
|
||||
exit 1
|
||||
fi
|
||||
|
||||
## Halt
|
||||
|
||||
if ! $VALGRIND $Q $JQ -n halt; then
|
||||
echo "jq halt didn't work as expected" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
if $VALGRIND $Q $VG_EXIT0 $JQ -n 'halt_error(1)'; then
|
||||
echo "jq halt_error(1) didn't work as expected" 1>&2
|
||||
exit 1
|
||||
elif [ $? -ne 1 ]; then
|
||||
echo "jq halt_error(1) had wrong error code" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
if $VALGRIND $Q $VG_EXIT0 $JQ -n 'halt_error(11)'; then
|
||||
echo "jq halt_error(11) didn't work as expected" 1>&2
|
||||
exit 1
|
||||
elif [ $? -ne 11 ]; then
|
||||
echo "jq halt_error(11) had wrong error code" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
if [ -n "$($VALGRIND $Q $JQ -n 'halt_error(1)' 2>&1)" ]; then
|
||||
echo "jq halt_error(1) had unexpected output" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
if [ -n "$($VALGRIND $Q $JQ -n '"xyz\n"|halt_error(1)' 2>/dev/null)" ]; then
|
||||
echo "jq halt_error(1) had unexpected output on stdout" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
if [ "$($VALGRIND $Q $JQ -n '"xyz\n"|halt_error(1)' 2>&1)" != xyz ]; then
|
||||
echo "jq halt_error(1) had unexpected output" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check $JQ_COLORS
|
||||
$JQ -Ccn . > $d/color
|
||||
printf '\033[1;30mnull\033[0m\n' > $d/expect
|
||||
cmp $d/color $d/expect
|
||||
JQ_COLORS='4;31' $JQ -Ccn . > $d/color
|
||||
printf '\033[4;31mnull\033[0m\n' > $d/expect
|
||||
cmp $d/color $d/expect
|
||||
JQ_COLORS='1;30:0;31:0;32:0;33:0;34:1;35:1;36' \
|
||||
$JQ -Ccn '[{"a":true,"b":false},123,null]' > $d/color
|
||||
(
|
||||
printf '\033[1;35m[\033[1;36m{'
|
||||
printf '\033[0m\033[34;1m"a"\033['
|
||||
printf '0m\033[1;36m:\033[0m\033['
|
||||
printf '0;32mtrue\033[0m\033[1'
|
||||
printf ';36m,\033[0m\033[34;1m'
|
||||
printf '"b"\033[0m\033[1;36m:\033'
|
||||
printf '[0m\033[0;31mfalse\033'
|
||||
printf '[0m\033[1;36m\033[1;36'
|
||||
printf 'm}\033[0m\033[1;35m,\033['
|
||||
printf '0;33m123\033[0m\033[1;'
|
||||
printf '35m,\033[1;30mnull\033'
|
||||
printf '[0m\033[1;35m\033[1;35'
|
||||
printf 'm]\033[0m\n'
|
||||
) > $d/expect
|
||||
cmp $d/color $d/expect
|
||||
|
||||
# Check garbage in JQ_COLORS. We write each color sequence into a 16
|
||||
# char buffer that needs to hold ESC [ <color> m NUL, so each color
|
||||
# sequence can be no more than 12 chars (bytes). These emit a warning
|
||||
# on stderr.
|
||||
set -vx
|
||||
echo 'Failed to set $JQ_COLORS' > $d/expect_warning
|
||||
$JQ -Ccn '[{"a":true,"b":false},123,null]' > $d/expect
|
||||
JQ_COLORS='garbage;30:*;31:,;3^:0;$%:0;34:1;35:1;36' \
|
||||
$JQ -Ccn '[{"a":true,"b":false},123,null]' > $d/color 2>$d/warning
|
||||
cmp $d/color $d/expect
|
||||
cmp $d/warning $d/expect_warning
|
||||
JQ_COLORS='1234567890123456789;30:0;31:0;32:0;33:0;34:1;35:1;36' \
|
||||
$JQ -Ccn '[{"a":true,"b":false},123,null]' > $d/color 2>$d/warning
|
||||
cmp $d/color $d/expect
|
||||
cmp $d/warning $d/expect_warning
|
||||
JQ_COLORS='1;31234567890123456789:0;31:0;32:0;33:0;34:1;35:1;36' \
|
||||
$JQ -Ccn '[{"a":true,"b":false},123,null]' > $d/color 2>$d/warning
|
||||
cmp $d/color $d/expect
|
||||
cmp $d/warning $d/expect_warning
|
||||
JQ_COLORS='1234567890123456;1234567890123456:1234567890123456;1234567890123456:1234567890123456;1234567890123456:1234567890123456;1234567890123456:1234567890123456;1234567890123456:1234567890123456;1234567890123456:1234567890123456;1234567890123456' \
|
||||
$JQ -Ccn '[{"a":true,"b":false},123,null]' > $d/color 2>$d/warning
|
||||
cmp $d/color $d/expect
|
||||
cmp $d/warning $d/expect_warning
|
||||
JQ_COLORS="0123456789123:0123456789123:0123456789123:0123456789123:0123456789123:0123456789123:0123456789123:0123456789123:" \
|
||||
$JQ -Ccn '[{"a":true,"b":false},123,null]' > $d/color 2>$d/warning
|
||||
cmp $d/color $d/expect
|
||||
cmp $d/warning $d/expect_warning
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,3 @@
|
||||
def fill:"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
|
||||
def e:"日本語";
|
||||
e == "日本語"
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
|
||||
. "${0%/*}/setup" "$@"
|
||||
|
||||
if [ "$($VALGRIND $Q $JQ -nf $JQTESTDIR/utf8-truncate.jq)" != "true" ]; then
|
||||
echo "UTF-8 byte sequences that span the jv_load_file read buffer are mangled"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit 0
|
||||
Reference in new issue
Block a user