Compare commits

...
Author SHA1 Message Date
William Langford 51fc9e02a5 fixup! Configure appveyor build to use python 2019-02-22 20:05:09 -05:00
William Langford 0d31bdc4cf fixup! Configure appveyor build to use python 2019-02-22 20:02:44 -05:00
William Langford 0960bfc06d fixup! Configure appveyor build to use python 2019-02-22 19:53:14 -05:00
William Langford 22e6f9e966 Change contains to return true for empty string needles
The behavior of memmem for an empty needle is inconsistent between
implementations of libc.
Our tests imply that we want an empty string needle to be true,
so check for an empty needle before calling memmem.
2019-02-22 19:50:26 -05:00
William Langford df8346a935 Configure appveyor build to use python 2019-02-22 19:50:26 -05:00
William Langford e1fb525796 Cleanup leftover references to ruby 2019-02-22 19:32:31 -05:00
William Langford da170faafe Configure travis build to use python 2019-02-22 19:32:31 -05:00
William Langford 03f746bf24 Remove ruby dependency from mantests 2019-02-22 19:32:31 -05:00
William Langford 5989388c9c Remove ruby dependency from manpage build 2019-02-22 19:32:31 -05:00
William Langford 208c668818 Remove ruby dependency from website build 2019-02-22 19:32:31 -05:00
Nico Williams 9a0d5be790 Merge pull request #1837 from muhmuhten/limit_0 2019-02-21 08:17:41 -06:00
Muh Muhten 130152a2ac Ensure limit(0; ...) is empty 2019-02-21 04:11:41 -05:00
Muh Muhten 730c730940 Rewrite _assign/2 using $argument syntax 2019-02-20 20:12:52 -06:00
Muh Muhten 85caac4473 Work around fgets()'s lack of length reporting
Stop losing \0s from the end of files because we don't know how much
fgets() read into our buffer. If we prefill the buffer with anything
other than zeroes, we can actually tell the difference between the
terminator and unused space in the buffer.

This is possibly more expensive, but at least it should be correct.

Fixes #1504
2019-02-20 20:12:52 -06:00
Nicolas Williams 048acee39b Fix if-then-else example formatting 2019-02-20 20:03:26 -06:00
Nicolas Williams 8a02b2c9c1 Document optional else clause of if 2019-02-20 20:03:26 -06:00
Chance Zibolski 4f6045a94e Support "if" without "else" clause
Defaults the else clause to the identity filter "." if an else clause is
missing.

The following two jq programs are considered equivalent:

```
if .foo then
    .foo = "bar"
else
    .
end
```

```
if .foo then
    .foo = "bar"
end
```
2019-02-20 20:03:26 -06:00
Muh Muhten 1ef90acb38 Bind data imports properly when already resolved 2019-02-20 20:02:41 -06:00
Muh Muhten e843a4fdf9 Make builtin binding fast again by binding only referenced symbols
Avoid doing the internal binding of top-level symbols in the parser,
leaving that work to be done in a post-processing step. For builtins,
this lets us do a reference-aware bind step (block_bind_incremental)
*after* generating builtins/0.

Libraries are a bit trickier since they may be bound multiple times, so
instead of thinking through the implications I added (block_bind_self)
to resolve all internal symbols immediately.
2019-02-20 19:43:13 -06:00
Muh Muhten b2b0bd37a1 Load ~/.jq as a library instead of with builtins
Remove the special code which loads ~/.jq in builtin.c, and instead glue
an optional include which points to the same file onto the main program
in linker.c.

Fixes a minor bug where errors in ~/.jq would be labelled <builtin>.
2019-02-20 19:16:18 -06:00
Muh Muhten 450ade4818 Add import metadata key "optional"
A library marked is imported if found, but silently skipped if missing.
This is the desired semantic for the auto-include at ~/.jq
2019-02-20 19:16:18 -06:00
Muh Muhten c814c4e001 Replace TOP-before-imports special case with assert
The case isn't actually possible afaict.
2019-02-20 19:16:18 -06:00
Muh Muhten 4f4494c770 Catch .. as the first component of a module path
Only the second and subsequent path components were being checked, which
I guess is theoretically security-relevant.

There's no apparent point to reconstructing the path after splitting it
by adding /s back in, either.
2019-02-20 19:16:18 -06:00
Muh Muhten ff014824bc Pass on the error message when rel_path is invalid
"Module path must be a string" is not a useful error message when the
reason the module path isn't a string is because the string it was got
replaced with an invalid with an error message for some other reason.

Also fixes a few memory leaks on early exits.
2019-02-20 19:16:18 -06:00
Muh Muhten 8edf58a95f Simplify definition of block_bind_referenced 2019-02-20 19:16:18 -06:00
Jason Hood 88e1f59ec1 Allow globbing on Windows
The mingw-w64 runtime expands wildcards, which causes a discrepancy
between `main`'s `argv` (multiple file names) and Windows' `wargv` (a
single wildcard).  Use `wmain` to retrieve the wide character args.
2019-02-17 23:58:23 -06:00
Jason Hood 80b064f97b Make UTC time on Windows
Windows has `_mkgmtime` for the UTC equivalent of `mktime`.
2019-02-16 20:42:25 -06:00
Jason Hood 0fc8200ab4 Add ISO 8601-specific strptime
Implement `strptime` for systems lacking it, but only supporting the
ISO 8601 format string.
2019-02-16 20:41:41 -06:00
Jason Hood 87594f81d6 Prevent redirecting to NUL crash
Windows regards the `NUL` device as tty, which causes `WriteFile` to
fail (the bytes written pointer cannot be `NULL` in this case).  Tweak
the color test to ensure tty is accurate.
2019-02-16 20:40:41 -06:00
Jason Hood def42a31ca Possibly enable color output on Windows (#1494)
Windows 10 supports color output, but it must be explicitly enabled;
ANSICON provides support for other versions.  Enable color if either of
those are available.  Resolves #1494.
2019-02-16 20:40:41 -06:00
Muh Muhten 605bfb3577 Reimplement block_drop_unreferenced in linear time 2019-02-16 20:30:20 -06:00
Nico Williams 9fa2e51099 Merge pull request #1818 from muhmuhten/free_in_index 2019-02-09 16:32:20 -06:00
Muh Muhten 3f59bee3e0 Make sure to free in jv_string_indexes 2019-02-09 17:17:59 -05:00
Muh Muhten 88613f5469 Improve linking time by marking subtrees with unbound symbols 2019-02-08 22:41:31 -06:00
Nicolas Williams 2660b04a73 Fix infinite loop in index("") (fix #1815) 2019-02-08 20:40:55 -06:00
Matt Clegg 18ac789d6d FIX if-then-else example
"(.+)" doesn't match new line characters so the link generated for [if-then-else](https://github.com/stedolan/jq/blob/90bc29c1b544c0436ec44246e180fdbb6d6384df/docs/content/3.manual/v1.5/manual.yml#L1804) doesn't work
2019-02-07 11:08:44 -06:00
Muh Muhten 956d40d9dd Define error/1 in terms of error/0
The parser generates a call to error/0 on break. Having that exported
from C directly removes a language dependency on builtin.jq.

In any case, error/0 seems to be the more fundamental operation, seeing
as the old C impementation of error/1 did nothing useful with its input.
2019-02-07 11:05:06 -06:00
Muh Muhten 2fd725209e Export input/0 directly from C, removing _input/0
The distinction between input/0 and _input/0 has been unnecessary since
a9c613..7dc34b, which moved end-of-input handling into C and replaced
the jq definition of input with `def input: _input;`
2019-02-06 12:06:03 -06:00
Derrick Pallas 21be3857b5 builtin/f_match: prevent overruns of input_string
If these values do not match exactly, it is because the UTF-8 is invalid
anyway and we counted codepoints differently than oniguruma did.  Perhaps it
would be better to error out here, but at least one similar loop already
uses < vs != and since we're off the rails anyway this might be OK.  It is
certainly better than overruning the buffer.

Resolves https://github.com/stedolan/jq/issues/1804
2019-02-04 14:12:03 -06:00
Derrick Pallas d29bf5b4fc builtin/f_match: call onig_error_code_to_str with einfo correctly 2019-02-02 16:17:12 -08:00
Muh Muhten 3c5b1419a2 Unify timegm fallbacks into my_mktime
my_timegm was introduced in 1900c7 to add a mktime fallback used in
4a6241, the subsequently removed in c53823. As a result of this code
shuffling, the jq mktime function (f_mktime) wound up using a fallback
even on systems where timegm is available, with incorrect results in
some time zones with DST, e.g.

% TZ=America/New_York jq -n '"2018-08-31T00:00:00Z"|fromdate|todate'
"2018-08-31T01:00:00Z"

This undoes 1900c7 and moves the workaround it added into the #else of
my_mktime.
2019-01-30 22:05:51 -06:00
Muh Muhten 012af2186b Fix build on BSD make
Replace uses of the nonstandard $^ variable by explicitly specifying the
source filenames. This is the only obvious thing preventing the build
from running correctly on non-GNU make.
2019-01-30 09:48:04 -06:00
Muh Muhten 12a628abdc Fix include/import namespace confusion 2019-01-30 09:47:24 -06:00
David Haguenauer 63ba2b1637 Add tests for built-in function contains 2019-01-22 11:00:08 -06:00
Nicolas Williams 61cd6dbb3b contains should handle embedded NULs (fix #1732) 2019-01-18 22:33:46 -06:00
William Langford 4b4fefa254 Merge pull request #1774 from dooglus/patch-1
Fix grammar typo.
2019-01-05 16:04:30 -05:00
Nicolas Williams 0bc77083ba Out-of-srcdir builds: AM_CPPFLAGS=-I${srcdir}/src 2018-12-26 12:53:48 -06:00
Ricardo Constantino b10878fea7 builtin: add missing windows.h include
Follow-up fix to b436156f5 because BOOL and DWORD
are not built-in to mingw64.
2018-12-26 12:53:48 -06:00
Ricardo Constantino 6893284ee5 Makefile.am: add no-undefined for win32
"Undefined symbols are not allowed in x86_64-w64-mingw32 shared libraries"

This was also applied in the official MINGW-packages repo:
Alexpux/MINGW-packages@b5566c58a
2018-12-26 02:45:54 +00:00
HE, Tao b436156f5b Mingw-w64 on windows doesn't have setenv, fix that.
Signed-off-by: HE, Tao <sighingnow@gmail.com>
2018-12-19 09:07:14 -06:00
Eugen fb1c635af6 Make fromstream/1 more efficient 2018-12-18 14:20:35 -06:00
Ryoichi KATO 6d3d2750ec fix --exit-code issues #1142 and #1139
* Set default error code to -4 in main(), Fixes #1142
* fix --exit-code with more than one object in input, Fixes #1139
    - Return code 1 or 4 based on last output, not last input.
2018-12-18 14:17:47 -06:00
mail6543210 712330940b Ensure that ${builddir}/src exists 2018-12-18 14:15:04 -06:00
William Langford 9c029f7077 Merge pull request #1779 from gvangool/update-NEWS
Adds the release info for 1.6 to NEWS
2018-12-15 15:20:23 -05:00
Gert Van Gool 2760fd365b Adds the release info for 1.6 to NEWS 2018-12-15 19:51:29 +01:00
Nicolas Williams 8ea4a5584e Allow variable refs as object keys {$key:value}
Users are often surprised by the requirement to parenthesize any
non-trivial object key expressions in object constructors.  E.g.,
{"a"+"b":1}.  This commit adds one more kind of key expression besides
literals and idents: variable references.

A common use case for this is jq programs as JSON templates to fill in
with variables computed from inputs or passed in on the command-line.
E.g., {some_key:$value}.  Now users can also use, e.g., {$key:$value}.

This and the restrictions on key and value expressions in object
constructors are now clarified a bit in the documentation.
2018-12-10 21:54:59 -06:00
Chris Moore 9caa1ba146 Fix grammar typo.
'operator' needs to be plural here.
2018-11-29 05:44:05 -08:00
William Langford bb87e6e30f Merge pull request #1768 from haochenx/readme-add-automake-as-build-dependency-in
add automake as build dependency to README.md
2018-11-20 10:13:47 -05:00
William Langford a1f1231a73 Add missing jv_copy when printing with -ar 2018-11-20 09:58:25 -05:00
Haochen Xie 0ab71031a6 add automake as build dependency to README.md 2018-11-12 16:00:53 -05:00
William Langford 2f2d05b087 Minor website fixes for 1.6 2018-11-02 08:15:18 -04:00
William Langford 2e01ff1fb6 Release jq-1.6 2018-11-01 21:49:29 -04:00
William Langford 3daecb246a Update website with details for jq-1.6 2018-11-01 21:48:34 -04:00
William Langford cd4f61aee6 Fix website whitespace 2018-10-21 23:28:43 -04:00
William Langford 39a3e2c46f Add documentation for destructuring alternation 2018-10-21 23:28:43 -04:00
Alex Ozdemir 0c845aa291 Bugfix: Math function checking
We had config machinery that determined which math functions are
available in libc. If a c math function was missing on the host system,
then the corresponding jq function would be removed from the source,
enabling the build to proceed anyway. The detection machinery was broken
in a subtle way, as was shown after glibc updated to 2.27, dropping the
`pow10` function. This caused compilation to fail.

The essential problem was that we detected whether a math function was
available by compiling and linking a small program evaluating that
function on constants. However, since gcc's optimization machinery has
special knowledge of some math functions (e.g. `pow10`), it can
optimize them away, even if they don't exist in the library and are not
linkable. That is, the following example compiles and links against
glibc 2.27, even though `pow10` has been removed:
```
int main () {
  printf("%f", pow10(0.5));
  return 0;
}
```

What?!
On the other hand, this program does not link:
```

int main () {
  double f;
  printf("%f", &f);
  printf("%f", pow10(f));
  return 0;
}
```

In the first program the call to `pow10` can be optimized away as a
constant expression. This requires GCC to know about `pow10` (which it
does!), but it does not require `pow10` to be in the library (and
actually linkable).

The solution is to use autoconf's machinery for detecting function
presence, instead of our own (buggy) machinery. This has the added
benefit of simplifying the code.

The bug was reported in issue #1659
2018-10-12 16:23:09 -04:00
William Langford 341a5fcab3 Fix osx travis build 2018-08-30 22:03:32 -04:00
William Langford dd4cec664c fix memory leak 2018-08-30 21:09:04 -04:00
William Langford 46d1ce2667 Restore JV_PRINT_COLOUR as an alias
JV_PRINT_COLOUR was part of the public libjq headers and was removed as
part of 2d05b54. While JV_PRINT_COLOR is definitely the preferred
spelling this side of the pond, we shouldn't just remove otherwise
exposed enum values.
2018-08-17 23:23:10 -04:00
William Langford 0673dee1b3 Fix destructuring alternation
Attempting to use the existing FORK_OPT opcode resulted in difficulty
knowing when to pop an error message off the stack and when not to. This
commit makes DESTRUCTURE_ALT a real opcode that is identical to
FORK_OPT, except for never pushing the error message onto the stack when
continuing from an error backtrack.

Some small changes were necessary to the DUP/POP behavior surrounding
destructuring to accomodate this.
2018-08-17 23:15:48 -04:00
William Langford 3dc5f4e948 Create issue template 2018-08-17 09:15:08 -04:00
Roland C. Dowdeswell 90bc29c1b5 jv_file.c: check to see if the file is a directory and fail
Not all stdio implementations disallow one to open a directory with
fopen(3) and so we specifically check for directories as it is also
in the standard search path.
2018-05-11 19:32:25 -05:00
Nicolas Williams 7fd9e86ea6 Make limit/2 more efficient
Contributed by @pkoppstein.
2018-03-06 16:35:33 -06:00
Nicolas Williams e113c67d51 limit/2 evals exp one time too many 2018-03-06 11:29:13 -06:00
rain1 bf88c73c20 src/main.c: fix issue #1587 by using jv_dumpf in the raw-output and ascii-output case. retain fwrite in the raw-output case. 2018-03-01 14:33:47 -06:00
Riley Avron ae11f111a5 Fix "provides" typo 2018-02-23 14:10:47 -06:00
David Fetter 2ce1e323df Change backticks to the more legible $() 2018-02-20 15:54:21 -05:00
Jakub Jirutka 68d0048148 Build static binaries and deploy to GH Releases from Travis 2018-02-20 12:11:28 -05:00
Jakub Jirutka bafb43e589 Build and run tests against musl libc using Alpine Linux on Travis 2018-02-20 12:11:28 -05:00
Jakub Jirutka c163e88af3 Change Travis config to utilize Build Stages
See: https://docs.travis-ci.com/user/build-stages/
2018-02-20 12:11:28 -05:00
Paul Chvostek bd34b8dd2e fix FreeBSD install instructions 2018-01-04 15:50:20 -06:00
Larry Aasen 61edf3fa93 Updated the compile-ios.sh script to fix issues with local oniguruma path. 2017-12-12 18:27:59 -06:00
Nicolas Williams 8eff744eec Update AUTHORS 2017-12-11 12:22:02 -06:00
David Fetter b4742c1257 Added rawfile
In passing, clean remnants of argfile from slurpfile docs.
2017-12-11 12:17:17 -06:00
Nicolas Williams 9a4576e756 Revert "reduce: handle empty updates (fix #1313)"
This reverts commit e24af3c78e.

While the semantics are desirable, there is no way to implement them
efficiently.  The reason is that in order to handle backtracking (empty)
from the state update expression, we have to retain a reference to the
reduction state value in order to restore it upon backtracking.
Retaining a reference to the reduction state kills performance by
causing lots of additional memory allocations and garbage because the
input to the update expression will always have at least two references,
thus no changes to it can be done in-place, and all changes end up being
CoW changes.

Avoiding this is the very reason for the LOADVN instruction (leaving
`null` in the variable loaded from).
2017-12-11 11:20:16 -06:00
Nicolas Williams b5560d8420 Fix import as $data linking 2017-12-10 23:09:35 -06:00
William Langford 79ece35981 Fix hang for slurped inputs with trailing newline 2017-12-04 20:10:56 -05:00
William Langford f06deb828a Fix #1534 2017-12-04 19:20:58 -05:00
William Langford 0c9eacedaa Actually fix the strptime tests
This has been a complicated issue to fix for a number of reasons.
The core of it is that the behavior is different between different
versions of macOS, some of which set possible-but-incorrect values.

This commit addresses the issue by always using our computation for
tm_wday and tm_yday on macOS. As a side-effect, strptime format
strings that specify %u and %j will no longer work on macOS.
2017-11-29 20:47:56 -05:00
Eric Bréchemier 476b36770d Keep object keys in parsing order in tostream output
As noted by @nicowilliams, `tostream` used `keys`,
which sorts the keys in alphabetical order, instead
of `keys_unsorted`, which preserves the parsing order.

Fixes #1541.
2017-11-29 11:46:08 -06:00
Andrew Speed 239278fd3a Use rvm to install ruby 1.9.3 2017-11-28 08:51:38 -05:00
William Langford 98b36e74b8 Fix strptime tests on macOS 10.12
Dates in 1900 are before the Unix epoch. We shouldn't make any promises
about how well they are supported, especially given that our time
support is a thin wrapper over the libc functions.

This changes the test to use dates after the epoch, which should fit
within both a signed and an unsigned 32-bit time_t.
2017-11-27 22:57:50 -05:00
William Langford c3cbbdd8a2 Fix memory leaks in various c-coded builtins 2017-11-27 20:37:14 -05:00
Nicolas Williams 18879fb993 Fix leak on error in strptime 2017-11-23 01:51:22 -06:00
trantor bdaddcf107 Typo correction
Suppresion -> Suppression
2017-11-22 13:47:35 -05:00
William Langford 7b81a836c3 Merge pull request #1403 from Argoday/master
Use unsorted keys in walk
2017-06-19 10:55:26 -04:00
Nicolas Williams c538237f4e Deal with strptime() on OS X and *BSD (fix #1415)
strptime() on OS X and *BSDs (reputedly) does not set tm_wday and
tm_yday unless corresponding %U and %j format specifiers were used.
That can be... surprising when one parsed year, month, and day anyways.
Glibc's strptime() conveniently sets tm_wday and tm_yday in those cases,
but OS X's does not, ignoring them completely.

This commit makes jq compute those where possible, though the day of
week computation may be wrong for dates before 1900-03-01 or after
2099-12-31.
2017-05-21 16:24:48 -05:00
Nicolas Williams 4a6241be06 Attempt to fix #1415
OS X (and *BSD) strptime() does not set tm_wday nor tm_yday unless
corresponding format options are used.  That means we must call timegm()
to set them.
2017-05-21 01:58:18 -05:00
Nicolas Williams 1900c7bcac Add private my_timegm() 2017-05-21 01:57:55 -05:00
Nicolas Williams 578d536233 Fix HAVE_TM_TM_GMT_OFF usage 2017-05-21 01:53:00 -05:00
Nicolas Williams b0d6d28310 Use AC_CHECK_MATH_FUNC() for all math functions 2017-05-21 01:22:39 -05:00
Nicolas Williams 10d66051ef Fix jv_load_file() assertion (fix #1410) 2017-05-20 18:53:48 -05:00
Daniel Lange cb3d5aff84 Improved git build instructions. 2017-05-11 09:05:56 -04:00
Jonathan Word ca12bd9b5d Use unsorted keys in walk
Preserve key sorting order when executing in walk, if sorted keys is desired `--sort-keys` should be used to explicitly obtain sorted keys.
2017-05-03 15:49:21 -04:00
Nico Williams 6d89e297fe Add JQ_COLORS env var for color config (fix #1252) 2017-04-29 14:00:52 -04:00
Nicolas Williams 32d8f2000a Fix build 2017-04-26 20:55:50 -05:00
Nicolas Williams 0bd7614bd5 Always use jv_mem_*alloc() 2017-04-21 18:41:06 -05:00
pkoppstein 4b4cf789e0 def isempty(g) # Testing 'isempty(empty)' at line number 1364 2017-04-15 16:45:00 -05:00
William Langford 76b1fc18f1 Update oniguruma to fix #1370 2017-04-02 14:58:59 -04:00
William Langford 405071337d Don't require java to build gh-pages 2017-03-31 18:48:28 -04:00
William Langford 151b494382 Add collect example with pipeline 2017-03-31 18:48:05 -04:00
William Langford 12f74b226e Add missing close-backtick in docs 2017-03-30 22:23:47 -04:00
Nicolas Williams 18753cb7a7 Also fix jn/2 and yn/2 2017-03-30 11:46:03 -05:00
Nicolas Williams 71d4ff6ed8 Fix frexp, modf, lgamma_r (fix #1374) 2017-03-29 00:07:42 -05:00
Nicolas Williams 7935d307f0 Allow . as {$a:$b} destructuring 2017-03-26 05:36:23 -05:00
Nicolas Williams 38bfec1594 Add flex/bison outputs 2017-03-26 05:36:23 -05:00
Nicolas Williams 2421dfb843 Add more destructuring tests 2017-03-26 05:36:22 -05:00
William Langford 94035be3fa Add --debug-trace=all for detailed debug output
When tracing execution, print the whole stack, not just the items used by the
current opcode
2017-03-26 05:36:22 -05:00
William Langford c8a2a0acc2 Fix a tripped assertion when generating reduces
A noop body, while useless, should still compile successfully
2017-03-26 05:36:22 -05:00
William Langford 3a8c8f4747 Add alternation destructuring operator ?//
This is a first pass to show the implementation.
It needs tests and evaluation, but doesn't break any existing tests.

NOT READY FOR MERGING
2017-03-26 05:36:22 -05:00
Nicolas Williams b142d484d5 Conditional exprs are not path exprs (fix #1368)
The conditional expression in if-then-elif-else-end cannot contribute to
path expressions because it doesn't change the input to any of the then/
elif/else expressions.  These must be generated via gen_subexp().

See also #1366.
2017-03-15 01:07:37 -05:00
Nicolas Williams 674e9fb7c2 Make and build dist in travis-CI (#1356) 2017-03-04 23:14:36 -06:00
Nicolas Williams 6878123202 Make configure.ac work for shallow clones 2017-03-04 22:57:35 -06:00
Nicolas Williams f7bf9a8e96 Make scripts/version work in shallow clones
Shallow clones don't have tags.  Use git describe --always --dirty in
such cases.

XXX Add branch name.
2017-03-03 14:30:58 -06:00
Nicolas Williams 89809a2d0f Fix make dist 2017-03-01 23:01:08 -06:00
Nicolas Williams 9df19f53e4 Fix off-by-one bug in #1108 fix 2017-03-01 22:45:02 -06:00
Nicolas Williams 65cbaac344 Array slice overflows (fix #1108) 2017-03-01 17:48:45 -06:00
Nicolas Williams 2fcb257168 getpath/1 should be a path expression (fix #1358)
It needs to be possible to do something like

    getpath($paths[]) += 1

meaning: increment all the paths in . that are listed in $paths[].

In order to do this getpath() needs to update the jq->path and
jq->value_at_path as necessary.
2017-02-28 21:54:30 -06:00
William Langford 63791b795a Fix name of builtins in the builtins/0 output 2017-02-26 19:50:10 -05:00
Nicolas Williams ea90b947b0 Fix warning about environ 2017-02-26 18:22:30 -06:00
Nicolas Williams 9a0d796298 Fix --without-oniguruma build 2017-02-26 18:22:23 -06:00
Nicolas Williams 0337027d54 Fix environ Win32 build problem 2017-02-26 18:06:35 -06:00
Nicolas Williams 8ea21a54ad Add halt, halt_error builtins (fix #386) 2017-02-26 16:34:56 -06:00
Nicolas Williams 6bac4ed059 Document stderr 2017-02-26 16:34:56 -06:00
Nicolas Williams a03ae02f44 Fix memory leak 2017-02-26 16:33:50 -06:00
Nicolas Williams 45f126514c Fix Appveyor for non-master branches 2017-02-26 16:13:22 -06:00
Nicolas Williams b279713e47 fixup 2017-02-26 16:12:57 -06:00
Nicolas Williams 3504bd9a2a Add $ENV builtin variable to access environment 2017-02-26 00:39:24 -06:00
Nicolas Williams 1a8a0ae0f5 Fix error message for undefined vars 2017-02-26 00:37:28 -06:00
Nicolas Williams 441b066dd8 Preserve precious vars in configure.ac (fix #795) 2017-02-25 21:49:38 -06:00
Nicolas Williams ef87010059 Document $ARGS and friends 2017-02-25 19:21:06 -06:00
Nicolas Williams 66fb962a66 Add $ARGS, --args and --jsonargs (fix #1345) 2017-02-25 19:13:28 -06:00
Nicolas Williams e8abc0a2f6 jq_compile_args(): allow object args to be object 2017-02-25 19:13:28 -06:00
Nicolas Williams 7b1bd99de6 Add jv_object_has() 2017-02-25 19:13:28 -06:00
Nicolas Williams 8619c6e060 Make test/shtest test of constant folding robust
Rather than testing that a constant expression produces so many
instructions, test that a variety of of such expressions produce the
same number of instructions.  This will make future changes in the
compiler less likely to break this test.
2017-02-25 19:13:28 -06:00
Nicolas Williams 2e1f2db8b5 Improve Appveyor build
Among other things, make TESTS=... breaks when using modules/oniguruma,
so set SUBDIRS= then.
2017-02-25 18:57:38 -06:00
Nicolas Williams ba32b33796 Windows: configure fails to detect missing math
For some reason AC_CHECK_MATH_FUNC() on Windows is not detecting a
handful of math functions as not being supported.  This commit is a bit
of a hack.
2017-02-25 18:51:09 -06:00
Nicolas Williams 6e77b017c5 Make comment consistent with code 2017-02-25 18:51:09 -06:00
Yasuhiro Matsumoto 5d058fa416 Don't call SetConsoleOutputCP
Should use wide-string function instead of SetConsoleOutputCP.

Fixes #1121
2017-02-25 18:51:04 -06:00
Yasuhiro Matsumoto 12c60b2f32 Quiet a compiler warning 2017-02-25 18:49:22 -06:00
Nicolas Williams 9801de6979 Windows: Also use -lshlwapi for jq(1) 2017-02-25 14:49:20 -06:00
Nicolas Williams c695f2cd9c When using builtin Oniguruma, set flags correctly
This should fix the Travis-CI build.
2017-02-25 14:11:42 -06:00
Nicolas Williams 27c29aeb7b Document localtime and strflocaltime 2017-02-23 23:39:24 -06:00
Nicolas Williams 06f20603f6 Add localtime and strflocaltime (fix #1349) 2017-02-23 22:26:53 -06:00
Nicolas Williams 607a9e3912 Improve manual section on assignment forms 2017-02-23 22:17:54 -06:00
Nicolas Williams d3b4ad04f5 Revert e7caf68 for Dockerfile 2017-02-23 22:01:43 -06:00
Nicolas Williams e7caf68edd Attempt to use builtin Oniguruma for CIs 2017-02-23 00:33:08 -06:00
Nicolas Williams c6374b6a1f Allow ./configure --with-oniguruma=builtin
This allows one to force the use of jq's builtin copy of Oniguruma
(builtin as a git submodule).

We still need to fix make dist to include that copy of Oniguruma.
2017-02-23 00:30:13 -06:00
Nicolas Williams 8cde328007 Allow var bindings in path expressions (#1347)
The expression in a variable binding is not and must not be seen as
being part of a path expression in which the variable binding appears.
2017-02-21 18:11:35 -06:00
Erik Brinkman 02bad4b298 Add local oniguruma submodule
Configure should still allow use of prebuilt onigurumas (whether
system-installed or in a special prefix).  If these are not found, and
configure was not called with `--without-oniguruma`, then use the vendored
oniguruma module.  If configure was called with `--without-oniguruma`, then we
do not build regex functionality into jq.
2017-02-18 21:34:26 -05:00
Nicolas Williams 9b2179089b Improve manual 2017-02-15 11:48:39 -06:00
William Langford 1c806b74ea Add builtins builtin 2017-02-13 11:36:20 -05:00
William Langford 8001955fb7 Fix memory leak in libm cfunctions 2017-02-13 11:36:20 -05:00
William Langford e6fa039f3f Merge pull request #1089 from l8nite/base64_decode 2017-02-12 16:25:44 -05:00
Shaun Guth e0b784ac6d Add @base64d for decoding base64 #47 2017-02-12 16:03:59 -05:00
J Phani Mahesh dc679081fa fix truncate_stream to match docs 2017-02-07 21:30:55 -06:00
Nicolas Williams 597c1f6667 Add more missing math functions 2017-02-04 00:11:46 -06:00
Nicolas Williams 125071cf00 Fix handling of unsupported math functions 2017-02-04 00:11:10 -06:00
Nicolas Williams 2fb099e4cf Promote wiki more in README 2017-01-31 16:13:32 -06:00
Nicolas Williams 6f9646a44f Improve docs somewhat, inspired by #1326 2017-01-30 14:11:05 -06:00
Nicolas Williams bd7b48c1b9 Make |= delete LHS when RHS is empty (Fix #1314)
Now that #1313 is fixed, |= no longer outputs null when the RHS update
expression outputs empty.

When a user wants to keep the current value of the LHS they would have
the RHS update expression output `.`, so having `empty` achieve the same
thing would be redundant.  The obvious thing to do is to delete the LHS
when the RHS update outputs `empty` (i.e., doesn't output any values).

It's reasonable to think that existing programs won't be broken by this
change, because reduce and |= not handling empty well is clearly a bug.
(Though it's possible that some programs were using empty to quickly
terminate reduce or |=, it's not likely.  They should use label/break
instead.)

Prior to this change |= would use the _last_ value output by the RHS
update expression.  With this change |= will use the _first_ value
instead.  This change _is_ a minor backwards-incompatible change.  It
may or may not be acceptable; we'll see.  It is a useful change in that
it makes |= faster when the update expression produces multiple values.
2017-01-30 14:11:05 -06:00
Nicolas Williams aac8132f47 recurse (i.e., ..) needs to find leaf nulls 2017-01-30 14:11:05 -06:00
Nicolas Williams 7bdf3a39ad Add SQL-style operators (#1322) 2017-01-30 14:11:05 -06:00
Nicolas Williams 396543191c Make first(g) more efficient: extract only 1 value
first(g) was extracting two values, which, if g is slow, made first(g)
slow.  And if the second extraction were to throw an error, then
first(g) would throw that error, which is clearly not the right thing to
do.  Besides that, first(g) was allocating garbage, which it no longer
does.
2017-01-30 14:11:05 -06:00
Nicolas Williams e24af3c78e reduce: handle empty updates (fix #1313) 2017-01-30 14:11:05 -06:00
Attila Fülöp e82147bbb8 Solaris needs XPG6 for c99 2017-01-28 19:16:23 -06:00
William Langford d2c3cf49e1 Merge pull request #1317 from wtlangford/dont-split-utf8-sequences
Handle cut-off UTF-8 sequences when reading files
2017-01-27 21:45:15 -05:00
William Langford e84d17196c Handle cut-off UTF-8 sequences when reading files
Read additional bytes from the file to complete the UTF-8 sequence so the bytes
in it don't get converted to U+FFFD replacement characters.
2017-01-27 21:04:26 -05:00
W-Mark Kubacki fd4ae8304e Parse no deeper than MAX_PARSING_DEPTH
while true; do printf '{"deeper": '; done | jq .
2017-01-27 09:59:44 -06:00
W-Mark Kubacki 83e2cf607f Skip printing what's below a MAX_PRINT_DEPTH
This addresses #1136, and mitigates a stack exhaustion when printing
a very deeply nested term.
2017-01-27 09:59:44 -06:00
Jakub Wilk 61b75639a7 gen_utf8_tables: remove unused variable 2017-01-26 13:15:59 -06:00
Jan Schulz 19d266b528 Revert "Disable appveyor build for now"
This reverts commit 0b8218515e.

There is a new oniguruma package which previously trashed the build.
2017-01-23 16:28:01 -06:00
Richard Groves 9c4238c2f6 Typo fixed
simply -> simple
2017-01-23 16:21:37 -06:00
Janne Cederberg 114ac72169 Documentation clarification suggestions 2017-01-23 15:48:44 -06:00
James Pearson Hughes a10d7fbb02 Update download instructions for Arch
jq has been in the community repo for [just over a year now][0].

[0]: https://projects.archlinux.org/svntogit/community.git/commit/trunk?h=packages/jq&id=8d5e06065bc92bf56ea651970c9bda07fb545554
2017-01-23 15:46:17 -06:00
Tim McCormack f33af0b16a Typo: s/more/mode/ in --seq 2017-01-23 15:45:07 -06:00
Clément MATHIEU eeba7b3167 Fix input_(line_number|filename) leak memory 2017-01-23 15:18:51 -06:00
Jay Satiro e5c30e6fae Fix cross-compile tmp dir removal
- cd out of the tmp dir before attempting to remove it.
2017-01-23 15:10:57 -06:00
Nicolas Williams 0b8218515e Disable appveyor build for now 2016-03-21 18:59:52 -05:00
David Tolnay 1a8a5ee000 Remove David from maintainers 2016-02-24 00:39:56 -08:00
Nicolas Williams 1740fd036d Travis-CI build for OSX (fix #1083) 2016-01-19 00:19:24 -06:00
Nicolas Williams 6c635998f8 Always test sort_by/group_by post-#1082 2016-01-18 12:17:58 -06:00
88 changed files with 9375 additions and 2777 deletions

No files matched your search

+29
View File
@@ -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.
+3
View File
@@ -0,0 +1,3 @@
[submodule "modules/oniguruma"]
path = modules/oniguruma
url = https://github.com/kkos/oniguruma.git
+187 -36
View File
@@ -2,53 +2,204 @@ 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
python: 3.6
addons:
apt:
packages:
- libonig-dev
- valgrind
addons:
apt:
packages:
- valgrind
- bison
- automake
before_install:
- rm src/{lexer,parser}.{c,h}
before_install:
- uname -s
- 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:
- pyenv global 3.6
- pip3 install pipenv
- pushd docs && pipenv sync && popd
- 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 bison
- rm src/{lexer,parser}.{c,h}
- sed -i.bak '/^AM_INIT_AUTOMAKE(\[-Wno-portability 1\.14\])$/s/14/11/' modules/oniguruma/configure.ac
install:
- pip3 install pipenv
- pushd docs && pipenv sync && popd
- 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 git libtool oniguruma-dev python3 python3-dev libxml2-dev libxslt-dev'
- /alpine/enter-chroot pip3 install pipenv
- alpine sh -c 'cd docs && pipenv sync'
before_script:
- autoreconf -if
script:
- alpine ./configure --disable-docs
- alpine make
- alpine make check
after_failure:
- cat test-suite.log
- cat tests/*.log
# 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
+3 -1
View File
@@ -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>
+5 -7
View File
@@ -13,9 +13,8 @@ RUN apt-get update && \
git \
bison \
flex \
ruby \
wget \
ruby-dev && \
python3 \
wget && \
wget https://github.com/kkos/oniguruma/archive/v5.9.6.tar.gz -O onig-5.9.6.tar.gz && \
sha512sum onig-5.9.6.tar.gz | grep 6b048d345e148c9da38af7a8df76d4a358eb3d4db91fa7834076e511f526a63544284f212b0d56badbbd33112c8b458a5fff02bfbbda012ecfe478bc436ea679 && \
tar zxvf onig-5.9.6.tar.gz && \
@@ -25,8 +24,8 @@ RUN apt-get update && \
./configure --prefix=/usr/local && \
make && \
make install ) && \
gem install bundler && \
(cd /app/docs && bundle install) && \
pip3 install pipenv && \
(cd /app/docs && pipenv sync) && \
(cd /app && \
autoreconf -i && \
./configure --disable-valgrind --enable-all-static --prefix=/usr/local && \
@@ -43,8 +42,7 @@ RUN apt-get update && \
bison \
git \
flex \
ruby \
ruby-dev && \
python3 && \
apt-get autoremove -y && \
rm -rf oniguruma-5.9.6 && \
rm -rf /var/lib/apt/lists/* /var/lib/gems
-5
View File
@@ -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
+38 -11
View File
@@ -18,6 +18,10 @@ LIBJQ_SRC = src/builtin.c src/bytecode.c src/compile.c src/execute.c \
AM_CFLAGS = -Wextra -Wall -Wno-missing-field-initializers \
-Wno-unused-parameter -Wno-unused-function
if WIN32
AM_CFLAGS += -municode
endif
ACLOCAL_AMFLAGS = -I config/m4
### Generating the lexer and parser
@@ -48,10 +52,11 @@ 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
libjq_la_LDFLAGS += -no-undefined
endif
include_HEADERS = src/jv.h src/jq.h
@@ -60,6 +65,8 @@ if ENABLE_UBSAN
AM_CFLAGS += -fsanitize=undefined
endif
AM_CPPFLAGS = -I$(srcdir)/src
### Running tests under Valgrind
if ENABLE_ASAN
@@ -97,11 +104,13 @@ generate_ver = ver="`{ $(srcdir)/scripts/version || echo '$(VERSION)' ; } | sed
.remake-version-h: .FORCE
@ $(generate_ver); test "x`cat src/version.h 2>/dev/null`" = "x$$ver" || touch .remake-version-h
src/version.h: .remake-version-h
mkdir -p src
$(AM_V_GEN) $(generate_ver); echo "$$ver" > $@
src/main.c: src/version.h
src/builtin.inc: src/builtin.jq
$(AM_V_GEN) sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/^/"/' -e 's/$$/\\n"/' $^ > $@
mkdir -p src
$(AM_V_GEN) sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' -e 's/^/"/' -e 's/$$/\\n"/' src/builtin.jq > $@
src/builtin.o: src/builtin.inc
bin_PROGRAMS = jq
@@ -109,13 +118,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)
@@ -123,16 +136,25 @@ TESTS_ENVIRONMENT = NO_VALGRIND=$(NO_VALGRIND)
man_MANS = jq.1
if ENABLE_DOCS
jq.1: $(srcdir)/docs/content/3.manual/manual.yml
$(AM_V_GEN) ( cd ${abs_srcdir}/docs; '$(BUNDLER)' exec rake manpage ) > $@ || { rm -f $@; false; }
jq.1: $(srcdir)/docs/content/manual/manual.yml
$(AM_V_GEN) ( cd ${abs_srcdir}/docs; pipenv run python build_manpage.py ) > $@ || { rm -f $@; false; }
jq.1.prebuilt: jq.1
$(AM_V_GEN) cp $^ $@ || { rm -f $@; false; }
$(AM_V_GEN) cp jq.1 $@ || { rm -f $@; false; }
else
jq.1: $(srcdir)/jq.1.prebuilt
$(AM_V_GEN) cp $^ $@
$(AM_V_GEN) cp $(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
@@ -143,12 +165,12 @@ install-binaries: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) install-exec
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
docs/Pipfile docs/Pipfile.lock docs/build_manpage.py \
docs/build_manpage.py docs/README.md 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 +180,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
+24
View File
@@ -1,6 +1,7 @@
Release history
* jq version 1.6 was released on Fri Nov 2 2018
* jq version 1.5 was released on Sat Aug 15 2015
* jq version 1.4 was released on Mon Jun 9 2014
* jq version 1.3 was released on Sun May 19 2013
@@ -8,6 +9,29 @@ Release history
* jq version 1.1 was released on Sun Oct 21 2012
* jq version 1.0 was released on Sun Oct 21 2012
New features in 1.6 since 1.5:
- Destructuring Alternation
- New Builtins:
- builtins/0
- stderr/0
- halt/0, halt_error/1
- isempty/1
- walk/1
- utf8bytelength/1
- localtime/0, strflocaltime/1
- SQL-style builtins
- and more!
- Add support for ASAN and UBSAN
- Make it easier to use jq with shebangs (8f6f28c)
- Add $ENV builtin variable to access environment
- Add JQ_COLORS env var for configuring the output colors
New features in 1.5 since 1.4:
- regular expressions (with Oniguruma)
+11 -7
View File
@@ -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, automake, 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.)
+18 -12
View File
@@ -1,23 +1,28 @@
environment:
matrix:
- MSYSTEM: MINGW64
PATH: C:\msys64\usr\bin;C:\msys64\mingw64\bin;C:\Windows\System32;C:\Windows;%PATH%
PATH: C:\Python36;C:\Python36\Scripts;C:\msys64\usr\bin;C:\msys64\mingw64\bin;C:\Windows\System32;C:\Windows;%PATH%
- MSYSTEM: MINGW32
PATH: C:\msys64\usr\bin;C:\msys64\mingw32\bin;C:\Windows\System32;C:\Windows;%PATH%
PATH: C:\Python36;C:\Python36\Scripts;C:\msys64\usr\bin;C:\msys64\mingw32\bin;C:\Windows\System32;C:\Windows;%PATH%
clone_script:
- bash -lc "echo $APPVEYOR_PULL_REQUEST_HEAD_REPO_NAME - $APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH"
- bash -lc "git clone -q --branch=${APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH:-$APPVEYOR_REPO_BRANCH} https://github.com/${APPVEYOR_PULL_REQUEST_HEAD_REPO_NAME:-$APPVEYOR_REPO_NAME}.git $APPVEYOR_BUILD_FOLDER"
- bash -lc "cd $APPVEYOR_BUILD_FOLDER && git checkout -qf ${APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH:APPVEYOR_REPO_BRANCH}"
- bash -lc "cd $APPVEYOR_BUILD_FOLDER && git submodule update --init --recursive"
install:
# update mysy2
# update msys2
- 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 -Su"
- C:\msys64\usr\bin\bash -lc "pacman --noconfirm -S autoconf automake bison flex git"
- C:\msys64\usr\bin\bash -lc "pip3 install --upgrade pipenv"
- C:\msys64\usr\bin\bash -lc "cd $APPVEYOR_BUILD_FOLDER/docs && ls && pipenv sync"
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 && make -j8"
- 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"
- 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
- bash -lc "exec 0</dev/null && cd $APPVEYOR_BUILD_FOLDER && pwd && ls -la"
@@ -26,9 +31,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 +47,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
View File
@@ -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 ]
+7 -2
View File
@@ -1,4 +1,9 @@
dnl AC_FIND_FUNC(func, arguments)
dnl AC_CHECK_MATH_FUNC(func)
AC_DEFUN([AC_CHECK_MATH_FUNC], [
AC_FIND_FUNC_NO_LIBS([$1], [m], [#include <math.h>], [$2])
AC_LANG(C)
AC_CHECK_LIB([m],[$1],[
eval "ac_tr_func=HAVE_[]upcase($1)"
AC_DEFINE_UNQUOTED($ac_tr_func)
],[
])
])
+125 -74
View File
@@ -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
@@ -103,7 +74,7 @@ dnl Code coverage
AC_ARG_ENABLE([gcov],
AC_HELP_STRING([--enable-gcov], [enable gcov code coverage tool]))
dnl Don't attempt to build docs if there's no Ruby lying around
dnl Don't attempt to build docs if python deps aren't installed
AC_ARG_ENABLE([docs],
AC_HELP_STRING([--disable-docs], [don't build docs]))
@@ -116,25 +87,25 @@ AC_ARG_ENABLE([all-static],
AC_HELP_STRING([--enable-all-static], [link jq with static libraries only]))
AS_IF([test "x$enable_docs" != "xno"],[
AC_CHECK_PROGS(bundle_cmd, bundle)
AC_CHECK_PROGS(pipenv_cmd, pipenv)
AC_CACHE_CHECK([for Ruby dependencies], [jq_cv_ruby_deps],
[jq_cv_ruby_deps=yes;
AS_IF([test "x$bundle_cmd" = "x" || \
! bmsg="`cd ${srcdir}/docs; "$bundle_cmd" check 2>/dev/null`"],[
AC_MSG_WARN([$bmsg])
AC_CACHE_CHECK([for Python dependencies], [jq_cv_python_deps],
[jq_cv_python_deps=yes;
AS_IF([test "x$pipenv_cmd" = "x" || \
! bmsg="`cd ${srcdir}/docs; LC_ALL=$LANG "$pipenv_cmd" check`"],[
AC_MSG_ERROR([$bmsg])
cat <<EOF
*****************************************************************
* Ruby dependencies for building jq documentation not found. *
* Python dependencies for building jq documentation not found. *
* You can still build, install and hack on jq, but the manpage *
* will not be rebuilt and some of the tests will not run. *
* See docs/README.md for how to install the docs dependencies. *
*****************************************************************
EOF
jq_cv_ruby_deps=no
jq_cv_python_deps=no
])])
if test "x$jq_cv_ruby_deps" != "xyes"; then
if test "x$jq_cv_python_deps" != "xyes"; then
enable_docs=no
fi
])
@@ -153,9 +124,12 @@ AC_FIND_FUNC([isatty], [c], [#include <unistd.h>], [0])
AC_FIND_FUNC([_isatty], [c], [#include <io.h>], [0])
AC_FIND_FUNC([strptime], [c], [#include <time.h>], [0, 0, 0])
AC_FIND_FUNC([strftime], [c], [#include <time.h>], [0, 0, 0, 0])
AC_FIND_FUNC([setenv], [c], [#include <stdlib.h>], [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>]])
@@ -175,35 +149,68 @@ if test $enable_pthread_tls = yes; then
fi
dnl libm math.h functions
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(atanh, [.5])
AC_CHECK_MATH_FUNC(cbrt, [.5])
AC_CHECK_MATH_FUNC(cos, [.5])
AC_CHECK_MATH_FUNC(cosh, [.5])
AC_CHECK_MATH_FUNC(exp, [.5])
AC_CHECK_MATH_FUNC(exp2, [.5])
AC_CHECK_MATH_FUNC(floor, [.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(log10, [.5])
AC_CHECK_MATH_FUNC(log2, [.5])
AC_CHECK_MATH_FUNC(pow, [2,2])
AC_CHECK_MATH_FUNC(remainder, [3,2])
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(y0, [.5])
AC_CHECK_MATH_FUNC(y1, [.5])
AC_CHECK_MATH_FUNC(acos)
AC_CHECK_MATH_FUNC(acosh)
AC_CHECK_MATH_FUNC(asin)
AC_CHECK_MATH_FUNC(asinh)
AC_CHECK_MATH_FUNC(atan2)
AC_CHECK_MATH_FUNC(atan)
AC_CHECK_MATH_FUNC(atanh)
AC_CHECK_MATH_FUNC(cbrt)
AC_CHECK_MATH_FUNC(ceil)
AC_CHECK_MATH_FUNC(copysign)
AC_CHECK_MATH_FUNC(cos)
AC_CHECK_MATH_FUNC(cosh)
AC_CHECK_MATH_FUNC(drem)
AC_CHECK_MATH_FUNC(erf)
AC_CHECK_MATH_FUNC(erfc)
AC_CHECK_MATH_FUNC(exp10)
AC_CHECK_MATH_FUNC(exp2)
AC_CHECK_MATH_FUNC(exp)
AC_CHECK_MATH_FUNC(expm1)
AC_CHECK_MATH_FUNC(fabs)
AC_CHECK_MATH_FUNC(fdim)
AC_CHECK_MATH_FUNC(floor)
AC_CHECK_MATH_FUNC(fma)
AC_CHECK_MATH_FUNC(fmax)
AC_CHECK_MATH_FUNC(fmin)
AC_CHECK_MATH_FUNC(fmod)
AC_CHECK_MATH_FUNC(frexp)
AC_CHECK_MATH_FUNC(gamma)
AC_CHECK_MATH_FUNC(hypot)
AC_CHECK_MATH_FUNC(j0)
AC_CHECK_MATH_FUNC(j1)
AC_CHECK_MATH_FUNC(jn)
AC_CHECK_MATH_FUNC(ldexp)
AC_CHECK_MATH_FUNC(lgamma)
AC_CHECK_MATH_FUNC(log10)
AC_CHECK_MATH_FUNC(log1p)
AC_CHECK_MATH_FUNC(log2)
AC_CHECK_MATH_FUNC(log)
AC_CHECK_MATH_FUNC(logb)
AC_CHECK_MATH_FUNC(modf)
AC_CHECK_MATH_FUNC(lgamma_r)
AC_CHECK_MATH_FUNC(nearbyint)
AC_CHECK_MATH_FUNC(nextafter)
AC_CHECK_MATH_FUNC(nexttoward)
AC_CHECK_MATH_FUNC(pow10) # Not available with glibc version >= 2.27
AC_CHECK_MATH_FUNC(pow)
AC_CHECK_MATH_FUNC(remainder)
AC_CHECK_MATH_FUNC(rint)
AC_CHECK_MATH_FUNC(round)
AC_CHECK_MATH_FUNC(scalb)
AC_CHECK_MATH_FUNC(scalbln)
AC_CHECK_MATH_FUNC(significand)
AC_CHECK_MATH_FUNC(sin)
AC_CHECK_MATH_FUNC(sinh)
AC_CHECK_MATH_FUNC(sqrt)
AC_CHECK_MATH_FUNC(tan)
AC_CHECK_MATH_FUNC(tanh)
AC_CHECK_MATH_FUNC(tgamma)
AC_CHECK_MATH_FUNC(trunc)
AC_CHECK_MATH_FUNC(y0)
AC_CHECK_MATH_FUNC(y1)
AC_CHECK_MATH_FUNC(yn)
dnl Thread local storage
have___thread=no
@@ -224,10 +231,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
-3
View File
@@ -1,6 +1,3 @@
.DS_Store
.sass-cache
output/*
# Autogenerated from public/css/base.scss
public/css/base.css
-7
View File
@@ -1,7 +0,0 @@
source "https://rubygems.org"
gem "rake"
gem "bonsai"
gem "maruku"
gem "ronn"
gem "mustache", "<1.0" # to support ruby <2.0
-63
View File
@@ -1,63 +0,0 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (4.2.3)
i18n (~> 0.7)
json (~> 1.7, >= 1.7.7)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
addressable (2.3.8)
bonsai (1.4.9)
activesupport (>= 3.0.3)
builder (>= 3.0.0)
i18n (>= 0.5.0)
launchy (>= 0.3.7)
liquid (>= 2.2.2)
maruku (>= 0.6.0)
rack
sass
sinatra (>= 1.0)
tilt (>= 1.3)
watch (>= 0.1.0)
yui-compressor
builder (3.2.2)
hpricot (0.8.6)
i18n (0.7.0)
json (1.8.3)
launchy (2.4.3)
addressable (~> 2.3)
liquid (3.0.6)
maruku (0.7.2)
minitest (5.8.0)
mustache (0.99.8)
rack (1.6.4)
rack-protection (1.5.3)
rack
rake (10.4.2)
rdiscount (2.1.8)
ronn (0.7.3)
hpricot (>= 0.8.2)
mustache (>= 0.7.0)
rdiscount (>= 1.5.8)
sass (3.4.16)
sinatra (1.4.6)
rack (~> 1.4)
rack-protection (~> 1.4)
tilt (>= 1.3, < 3)
thread_safe (0.3.5)
tilt (2.0.1)
tzinfo (1.2.2)
thread_safe (~> 0.1)
watch (0.1.0)
yui-compressor (0.12.0)
PLATFORMS
ruby
DEPENDENCIES
bonsai
maruku
mustache (< 1.0)
rake
ronn
+12
View File
@@ -0,0 +1,12 @@
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
[packages]
jinja2 = "*"
pyyaml = "*"
markdown = "*"
lxml = "*"
+117
View File
@@ -0,0 +1,117 @@
{
"_meta": {
"hash": {
"sha256": "16a9ef323592a417142c30be17e58c3cf36dc2bfdbf4757106a796b22262a1ce"
},
"pipfile-spec": 6,
"requires": {},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
"default": {
"jinja2": {
"hashes": [
"sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd",
"sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"
],
"index": "pypi",
"version": "==2.10"
},
"lxml": {
"hashes": [
"sha256:0537eee4902e8bf4f41bfee8133f7edf96533dd175930a12086d6a40d62376b2",
"sha256:0562ec748abd230ab87d73384e08fa784f9b9cee89e28696087d2d22c052cc27",
"sha256:09e91831e749fbf0f24608694e4573be0ef51430229450c39c83176cc2e2d353",
"sha256:1ae4c0722fc70c0d4fba43ae33c2885f705e96dce1db41f75ae14a2d2749b428",
"sha256:1c630c083d782cbaf1f7f37f6cac87bda9cff643cf2803a5f180f30d97955cef",
"sha256:2fe74e3836bd8c0fa7467ffae05545233c7f37de1eb765cacfda15ad20c6574a",
"sha256:37af783c2667ead34a811037bda56a0b142ac8438f7ed29ae93f82ddb812fbd6",
"sha256:3f2d9eafbb0b24a33f56acd16f39fc935756524dcb3172892721c54713964c70",
"sha256:47d8365a8ef14097aa4c65730689be51851b4ade677285a3b2daa03b37893e26",
"sha256:510e904079bc56ea784677348e151e1156040dbfb736f1d8ea4b9e6d0ab2d9f4",
"sha256:58d0851da422bba31c7f652a7e9335313cf94a641aa6d73b8f3c67602f75b593",
"sha256:7940d5c2185ffb989203dacbb28e6ae88b4f1bb25d04e17f94b0edd82232bcbd",
"sha256:7cf39bb3a905579836f7a8f3a45320d9eb22f16ab0c1e112efb940ced4d057a5",
"sha256:9563a23c1456c0ab550c087833bc13fcc61013a66c6420921d5b70550ea312bf",
"sha256:95b392952935947e0786a90b75cc33388549dcb19af716b525dae65b186138fc",
"sha256:983129f3fd3cef5c3cf067adcca56e30a169656c00fcc6c648629dbb850b27fa",
"sha256:a0b75b1f1854771844c647c464533def3e0a899dd094a85d1d4ed72ecaaee93d",
"sha256:b5db89cc0ef624f3a81214b7961a99f443b8c91e88188376b6b322fd10d5b118",
"sha256:c0a7751ba1a4bfbe7831920d98cee3ce748007eab8dfda74593d44079568219a",
"sha256:c0c5a7d4aafcc30c9b6d8613a362567e32e5f5b708dc41bc3a81dac56f8af8bb",
"sha256:d4d63d85eacc6cb37b459b16061e1f100d154bee89dc8d8f9a6128a5a538e92e",
"sha256:da5e7e941d6e71c9c9a717c93725cda0708c2474f532e3680ac5e39ec57d224d",
"sha256:dccad2b3c583f036f43f80ac99ee212c2fa9a45151358d55f13004d095e683b2",
"sha256:df46307d39f2aeaafa1d25309b8a8d11738b73e9861f72d4d0a092528f498baa",
"sha256:e70b5e1cb48828ddd2818f99b1662cb9226dc6f57d07fc75485405c77da17436",
"sha256:ea825562b8cd057cbc9810d496b8b5dec37a1e2fc7b27bc7c1e72ce94462a09a"
],
"index": "pypi",
"version": "==4.3.1"
},
"markdown": {
"hashes": [
"sha256:c00429bd503a47ec88d5e30a751e147dcb4c6889663cd3e2ba0afe858e009baa",
"sha256:d02e0f9b04c500cde6637c11ad7c72671f359b87b9fe924b2383649d8841db7c"
],
"index": "pypi",
"version": "==3.0.1"
},
"markupsafe": {
"hashes": [
"sha256:048ef924c1623740e70204aa7143ec592504045ae4429b59c30054cb31e3c432",
"sha256:130f844e7f5bdd8e9f3f42e7102ef1d49b2e6fdf0d7526df3f87281a532d8c8b",
"sha256:19f637c2ac5ae9da8bfd98cef74d64b7e1bb8a63038a3505cd182c3fac5eb4d9",
"sha256:1b8a7a87ad1b92bd887568ce54b23565f3fd7018c4180136e1cf412b405a47af",
"sha256:1c25694ca680b6919de53a4bb3bdd0602beafc63ff001fea2f2fc16ec3a11834",
"sha256:1f19ef5d3908110e1e891deefb5586aae1b49a7440db952454b4e281b41620cd",
"sha256:1fa6058938190ebe8290e5cae6c351e14e7bb44505c4a7624555ce57fbbeba0d",
"sha256:31cbb1359e8c25f9f48e156e59e2eaad51cd5242c05ed18a8de6dbe85184e4b7",
"sha256:3e835d8841ae7863f64e40e19477f7eb398674da6a47f09871673742531e6f4b",
"sha256:4e97332c9ce444b0c2c38dd22ddc61c743eb208d916e4265a2a3b575bdccb1d3",
"sha256:525396ee324ee2da82919f2ee9c9e73b012f23e7640131dd1b53a90206a0f09c",
"sha256:52b07fbc32032c21ad4ab060fec137b76eb804c4b9a1c7c7dc562549306afad2",
"sha256:52ccb45e77a1085ec5461cde794e1aa037df79f473cbc69b974e73940655c8d7",
"sha256:5c3fbebd7de20ce93103cb3183b47671f2885307df4a17a0ad56a1dd51273d36",
"sha256:5e5851969aea17660e55f6a3be00037a25b96a9b44d2083651812c99d53b14d1",
"sha256:5edfa27b2d3eefa2210fb2f5d539fbed81722b49f083b2c6566455eb7422fd7e",
"sha256:7d263e5770efddf465a9e31b78362d84d015cc894ca2c131901a4445eaa61ee1",
"sha256:83381342bfc22b3c8c06f2dd93a505413888694302de25add756254beee8449c",
"sha256:857eebb2c1dc60e4219ec8e98dfa19553dae33608237e107db9c6078b1167856",
"sha256:98e439297f78fca3a6169fd330fbe88d78b3bb72f967ad9961bcac0d7fdd1550",
"sha256:bf54103892a83c64db58125b3f2a43df6d2cb2d28889f14c78519394feb41492",
"sha256:d9ac82be533394d341b41d78aca7ed0e0f4ba5a2231602e2f05aa87f25c51672",
"sha256:e982fe07ede9fada6ff6705af70514a52beb1b2c3d25d4e873e82114cf3c5401",
"sha256:edce2ea7f3dfc981c4ddc97add8a61381d9642dc3273737e756517cc03e84dd6",
"sha256:efdc45ef1afc238db84cb4963aa689c0408912a0239b0721cb172b4016eb31d6",
"sha256:f137c02498f8b935892d5c0172560d7ab54bc45039de8805075e19079c639a9c",
"sha256:f82e347a72f955b7017a39708a3667f106e6ad4d10b25f237396a7115d8ed5fd",
"sha256:fb7c206e01ad85ce57feeaaa0bf784b97fa3cad0d4a5737bc5295785f5c613a1"
],
"version": "==1.1.0"
},
"pyyaml": {
"hashes": [
"sha256:3d7da3009c0f3e783b2c873687652d83b1bbfd5c88e9813fb7e5b03c0dd3108b",
"sha256:3ef3092145e9b70e3ddd2c7ad59bdd0252a94dfe3949721633e41344de00a6bf",
"sha256:40c71b8e076d0550b2e6380bada1f1cd1017b882f7e16f09a65be98e017f211a",
"sha256:558dd60b890ba8fd982e05941927a3911dc409a63dcb8b634feaa0cda69330d3",
"sha256:a7c28b45d9f99102fa092bb213aa12e0aaf9a6a1f5e395d36166639c1f96c3a1",
"sha256:aa7dd4a6a427aed7df6fb7f08a580d68d9b118d90310374716ae90b710280af1",
"sha256:bc558586e6045763782014934bfaf39d48b8ae85a2713117d16c39864085c613",
"sha256:d46d7982b62e0729ad0175a9bc7e10a566fc07b224d2c79fafb5e032727eaa04",
"sha256:d5eef459e30b09f5a098b9cea68bebfeb268697f78d647bd255a085371ac7f3f",
"sha256:e01d3203230e1786cd91ccfdc8f8454c8069c91bee3962ad93b87a4b2860f537",
"sha256:e170a9e6fcfd19021dd29845af83bb79236068bf5fd4df3327c1be18182b2531"
],
"index": "pypi",
"version": "==3.13"
}
},
"develop": {}
}
+10 -15
View File
@@ -2,24 +2,19 @@ Documentation
=============
The jq website, manpages and some of the tests are generated from this
directory. The directory holds a [Bonsai](http://tinytree.info)
website, and the manual is a YAML file in `content/3.manual`.
directory. The manual is a YAML file in `content/manual`.
To build the documentation (including building the jq manpage), you'll
need a working Ruby setup. The easiest way to get one is to install
RVM and Ruby 1.9.3 like so:
need python3 and pipenv. You can install pipenv like so:
\curl -L https://get.rvm.io | bash -s stable --ruby=1.9.3
pip install pipenv
After that finishes installing, you'll need to make sure RVM is on
your path by doing `source $HOME/.rvm/scripts/rvm`, or just opening a
new shell. See <https://rvm.io> for more info on RVM.
Though, you may need to say pip3 instead, depending on your system. Once
you have pipenv installed, you can install the dependencies by running
`pipenv install` from the `docs` directory.
Once RVM is installed, you can install all the dependencies for jq's
documentation build by running this from the `docs` directory:
Once this is done, rerun `./configure` in the jq root directory and then
the Makefile will be able to generate the jq manpage.
bundle install
When bundle manages to install the dependencies, rerun `./configure`
in the jq root directory and then the Makefile will be able to
generate the jq manpage.
To build the website, run `pipenv run ./build_website.py` from inside
the `docs` directory.
-24
View File
@@ -1,24 +0,0 @@
current_dir = File.dirname(__FILE__)
rakefile_manual = File.expand_path(File.join(current_dir, "Rakefile.manual"))
rakefile_website = File.expand_path(File.join(current_dir, "Rakefile.website"))
desc "Build the manpage from the bonsai source of the manual"
task :manpage do
system %(#{$0} -f #{rakefile_manual} manpage)
end
desc "Collect jq unit test cases from the bonsai source of the manual"
task :mantests do
system %(#{$0} -f #{rakefile_manual} mantests)
end
desc "Build the website from the bonsai sources"
task :build do
system %(#{$0} -f #{rakefile_website} build)
end
desc "Serve a live view of the website on http://localhost:5000/jq/"
task :serve do
system %(#{$0} -f #{rakefile_website} serve)
end
-49
View File
@@ -1,49 +0,0 @@
require 'ronn'
require 'tempfile'
require 'yaml'
def load_manual
YAML::load(File.open("content/3.manual/manual.yml"))
end
task :manpage do
Tempfile.open "manpage" do |f|
manual = load_manual
f.puts manual['manpage_intro']
f.puts manual['body']
manual['sections'].each do |section|
f.puts "## #{section['title'].upcase}\n"
f.puts section['body']
f.puts ""
(section['entries'] || []).each do |entry|
f.puts "### #{entry['title']}\n"
f.puts entry['body']
f.puts ""
(entry['examples'] || []).each do |example|
f.puts " jq '#{example['program']}'"
f.puts " #{example['input']}"
f.puts " => #{example['output'].join(", ")}"
f.puts
end
end
f.puts ""
end
f.puts manual['manpage_epilogue']
f.close
puts Ronn::Document.new(f.path).convert('roff').gsub(/<\/?code>/,"")
end
end
task :mantests do
load_manual['sections'].each do |section|
(section['entries'] || []).each do |entry|
(entry['examples'] || []).each do |example|
puts example['program'].gsub("\n", " ")
puts example['input']
example['output'].each do |s| puts s end
puts
end
end
end
end
-66
View File
@@ -1,66 +0,0 @@
require 'bonsai'
require 'json'
require 'liquid'
require 'maruku'
module ExtraFilters
def markdownify(input)
Maruku.new(input).to_html
end
def search_id(input)
input.gsub(/`/, '')
end
def section_id(input)
input.gsub(/[^a-zA-Z0-9_]/, '')
end
def entry_id(input)
input.gsub(/[ `]/, '')
end
def no_paragraph(input)
input.gsub('<p>', '').gsub('</p>', '')
end
def json(input)
input.to_json
end
def unique(input)
@n = (@n || 0) + 1
input + @n.to_s
end
end
Liquid::Template.register_filter(ExtraFilters)
task :build do
Bonsai.root_dir = Dir.pwd
Bonsai::Exporter.publish!
end
task :serve do
begin
Bonsai.log "Press Control+C to quit"
require 'rack'
require 'sinatra'
require 'watch'
require 'launchy'
Bonsai.root_dir = Dir.pwd
server = fork {
app = Rack::Builder.app {
map "/jq" do
use Bonsai::StaticPassThrough, :root => Bonsai.root_dir + "/output", :urls => ["/"]
end
run Bonsai::DevelopmentServer
}
Rack::Handler.default.run(app, :Port => 5000) do
Launchy.open("http://localhost:5000/jq/")
end
}
Watch.new("{content,templates,public}/**/*") { Bonsai::Exporter.process! }
rescue Interrupt
Process.kill("QUIT", server)
Process.wait(server)
exit
end
end
+235
View File
@@ -0,0 +1,235 @@
#!/usr/bin/env python3
from datetime import date
from io import StringIO
from lxml import etree
import markdown
from markdown.extensions import Extension
import re
import sys
import yaml
# Prevent our markdown parser from trying to help by interpreting things in angle brackets as HTML tags.
class EscapeHtml(Extension):
def extendMarkdown(self, md, md_globals):
del md.preprocessors['html_block']
del md.inlinePatterns['html']
class RoffWalker(object):
def __init__(self, tree, output=sys.stdout):
self.tree = tree
self.target = output
self.f = StringIO()
def walk(self):
self._walk(self.tree, parent_tag=None)
# We don't want to start lines with \. because that can confuse man
# For lines that start with \., we need to prefix them with \& so it
# knows not to treat that line as a directive
data = re.sub(r'^\\\.', r'\&.', self.f.getvalue(), flags=re.MULTILINE)
self.target.write(data)
def _ul_is_special(self, root):
if len(root) != 1:
return False
child = root[0]
if child.tag != 'li':
return False
msg = ''.join(child.itertext()).strip()
return msg.endswith(':')
def _walk_child(self, root):
if len(root) > 0:
self._walk(root[0], parent_tag=root.tag)
def _write_element(self, root, ensure_newline=True):
if root.text is not None:
text = self._sanitize(root.text)
self.__write_raw(text)
self._walk_child(root)
self._write_tail(root, ensure_newline=ensure_newline)
def _write_tail(self, root, ensure_newline=False, inline=False):
if root.tail is not None:
if inline or root.tail != '\n':
text = self._sanitize(root.tail)
if text.endswith('\n'):
ensure_newline = False
self.__write_raw(text)
if ensure_newline:
self.__write_raw('\n')
def _walk(self, root, parent_tag=None):
last_tag = None
while root is not None:
if root.tag == 'h1':
self.__write_cmd('.TH "JQ" "1" "{}" "" ""'.format(date.today().strftime('%B %Y')))
self.__write_cmd('.SH "NAME"')
# TODO: properly parse this
self.__write_raw(r'\fBjq\fR \- Command\-line JSON processor' + "\n")
elif root.tag == 'h2':
self.__write_cmd('.SH "{}"'.format(''.join(root.itertext()).strip()))
elif root.tag == 'h3':
text = ''.join(root.itertext()).strip()
self.__write_cmd('.SS "{}"'.format(self._h3_sanitize(text)))
elif root.tag == 'p':
if last_tag not in ['h2', 'h3'] and parent_tag not in ['li']:
self.__write_cmd('.P')
self._write_element(root, ensure_newline=(parent_tag != 'li'))
elif root.tag == 'ul':
if self._ul_is_special(root):
li = root[0]
self.__write_cmd('.TP')
self._write_element(li)
next = root.getnext()
while next is not None and next.tag == 'p':
if next.getnext() is not None and next.getnext().tag == 'pre':
# we don't want to .IP these, because it'll look funny with the code indent
break
self.__write_cmd('.IP')
self._write_element(next)
root = next
next = root.getnext()
else:
self._walk_child(root)
self._write_tail(root)
# A pre tag after the end of a list doesn't want two of the indentation commands
if root.getnext() is None or root.getnext().tag != 'pre':
self.__write_cmd('.IP "" 0')
elif root.tag == 'li':
self.__write_cmd(r'.IP "\(bu" 4')
if root.text is not None and root.text.strip() != '':
text = self._sanitize(root.text)
self.__write_raw(text)
self._walk_child(root)
self._write_tail(root, ensure_newline=True)
elif root.tag == 'strong':
if root.text is not None:
text = self._sanitize(root.text)
self.__write_raw('\\fB{}\\fR'.format(text))
self._write_tail(root, inline=True)
elif root.tag == 'em':
if root.text is not None:
text = self._sanitize(root.text)
self.__write_raw('\\fI{}\\fR'.format(text))
self._write_tail(root, inline=True)
elif root.tag == 'code':
if root.text is not None:
text = self._code_sanitize(root.text)
self.__write_raw('\\fB{}\\fR'.format(text))
self._write_tail(root, inline=True)
elif root.tag == 'pre':
self.__write_cmd('.IP "" 4')
self.__write_cmd('.nf\n') # extra newline for spacing reasons
next = root
first = True
while next is not None and next.tag == 'pre':
if not first:
self.__write_raw('\n')
text = ''.join(next.itertext(with_tail=False))
self.__write_raw(self._pre_sanitize(text))
first = False
root = next
next = next.getnext()
self.__write_cmd('.fi')
self.__write_cmd('.IP "" 0')
else:
self._walk_child(root)
last_tag = root.tag
root = root.getnext()
def _base_sanitize(self, text):
text = re.sub(r'\\', r'\\e', text)
text = re.sub(r'\.', r'\\.', text)
text = re.sub("'", r"\'", text)
text = re.sub('-', r'\-', text)
return text
def _pre_sanitize(self, text):
return self._base_sanitize(text)
def _code_sanitize(self, text):
text = self._base_sanitize(text)
text = re.sub(r'\s', ' ', text)
return text
def _h3_sanitize(self, text):
text = self._base_sanitize(text)
text = re.sub(' \n|\n ', ' ', text)
text = re.sub('\n', ' ', text)
return text
def _sanitize(self, text):
text = self._base_sanitize(text)
text = re.sub(r'<([^>]+)>', r'\\fI\1\\fR', text)
text = re.sub(r' +', ' ', text)
text = re.sub('\n', ' ', text)
return text
def __write_cmd(self, dat):
print('.', dat, sep='\n', file=self.f)
pass
def __write_raw(self, dat):
print(dat, sep='', end='', file=self.f)
pass
def load_yml_file(fn):
with open(fn) as f:
return yaml.load(f)
def dedent_body(body):
lines = [re.sub(r'^ (\S)', r'\1', l) for l in body.split('\n')]
return '\n'.join(lines)
def convert_manual_to_markdown():
f = StringIO()
manual = load_yml_file("content/manual/manual.yml")
f.write(manual.get('manpage_intro', '\n'))
f.write(dedent_body(manual.get('body', '\n')))
for section in manual.get('sections', []):
f.write('## {}\n'.format(section.get('title', '').upper()))
f.write(dedent_body(section.get('body', '\n')))
f.write('\n')
for entry in section.get('entries', []):
f.write('### {}\n'.format(entry.get('title', '')))
f.write(dedent_body(entry.get('body', '\n')))
f.write('\n')
if entry.get('examples') is not None:
f.write("~~~~\n")
first = True
for example in entry.get('examples'):
if not first:
f.write('\n')
f.write("jq '{}'\n".format(example.get('program', '')))
f.write(" {}\n".format(example.get('input', '')))
output = [str(x) for x in example.get('output', [])]
f.write("=> {}\n".format(', '.join(output)))
first = False
f.write("~~~~\n")
f.write('\n')
f.write(manual.get('manpage_epilogue', ''))
return f.getvalue()
# Convert manual.yml to our special markdown format
markdown_data = convert_manual_to_markdown()
# Convert markdown to html
html_data = markdown.markdown(markdown_data, extensions=[EscapeHtml(), 'fenced_code'])
# Parse the html into a tree so we can walk it
tr = etree.HTML(html_data, etree.HTMLParser())
# Convert the markdown to ROFF
RoffWalker(tr).walk()
+13
View File
@@ -0,0 +1,13 @@
#!/usr/bin/env python3
import yaml
with open("content/manual/manual.yml") as f:
manual = yaml.load(f)
for section in manual.get('sections', []):
for entry in section.get('entries', []):
for example in entry.get('examples', []):
print(example.get('program', '').replace('\n', ' '))
print(example.get('input', ''))
for s in example.get('output', []):
print(s)
print('')
+62
View File
@@ -0,0 +1,62 @@
#!/usr/bin/env python3
import glob
import itertools
from jinja2 import Environment, FileSystemLoader, Markup, select_autoescape, contextfunction
from markdown import markdown
import os
import os.path
import re
import shutil
import yaml
env = Environment(
loader=FileSystemLoader('templates'),
autoescape=select_autoescape(['html.j2']),
)
def load_yml_file(fn):
with open(fn) as f:
return yaml.load(f)
env.filters['search_id'] = lambda input: input.replace(r'`', '')
env.filters['section_id'] = lambda input: re.sub(r"[^a-zA-Z0-9_]", '', input)
env.filters['entry_id'] = lambda input: re.sub(r"[ `]", '', input)
env.filters['markdownify'] = lambda input: Markup(markdown(input))
env.filters['no_paragraph'] = lambda input: Markup(re.sub(r"</?p>", '', input))
env.globals['unique_id'] = contextfunction(lambda ctx: str(next(ctx['unique_ctr'])))
env.globals.update(load_yml_file('site.yml'))
env.globals['navigation'] = ['tutorial', 'download', 'manual']
def generate_file(env, fname='content/1.tutorial/default.yml'):
path, base = os.path.split(fname)
path = os.path.relpath(path, 'content')
output_dir = os.path.join('output', path)
output_path = os.path.join(output_dir, 'index.html')
template_name = re.sub(r".yml$", '.html.j2', base)
ctx = load_yml_file(fname)
ctx.update(unique_ctr=itertools.count(1), permalink=path)
os.makedirs(output_dir, exist_ok=True)
env.get_template(template_name).stream(ctx).dump(output_path, encoding='utf-8')
def copy_public_files(root=''):
for f in os.scandir(os.path.join('public', root)):
src = os.path.join(root, f.name)
dst = os.path.join('output', src)
if f.is_dir():
os.makedirs(dst, exist_ok=True)
copy_public_files(src)
else:
shutil.copyfile(f.path, dst)
copy_public_files()
for fn in glob.glob('content/**/*.yml', recursive=True):
generate_file(env, fn)
@@ -27,11 +27,14 @@ 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.6 binaries for
[64-bit](https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64)
or
[32-bit](https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux32).
* jq 1.5 binaries for
[64-bit](https://github.com/stedolan/jq/releases/download/jq-1.5/jq-linux64)
@@ -53,6 +56,9 @@ body:
* Use [Homebrew](http://brew.sh/) to install jq 1.5 with
`brew install jq`.
* jq 1.6 binary for
[64-bit](https://github.com/stedolan/jq/releases/download/jq-1.6/jq-osx-amd64).
* jq 1.5 binary for
[64-bit](https://github.com/stedolan/jq/releases/download/jq-1.5/jq-osx-amd64).
@@ -68,8 +74,13 @@ 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
@@ -86,6 +97,11 @@ body:
* Use [Chocolatey NuGet](https://chocolatey.org/) to install jq 1.5 with
`chocolatey install jq`.
* jq 1.6 executables for
[64-bit](https://github.com/stedolan/jq/releases/download/jq-1.6/jq-win64.exe)
or
[32-bit](https://github.com/stedolan/jq/releases/download/jq-1.6/jq-win32.exe).
* jq 1.5 executables for
[64-bit](https://github.com/stedolan/jq/releases/download/jq-1.5/jq-win64.exe)
or
@@ -106,6 +122,8 @@ body:
SHA-256 checksums are provided for all release and pre-release binaries.
They can be found under
[sig/v1.x/sha256sum.txt](https://github.com/stedolan/jq/tree/master/sig).
The checksums for jq 1.6 are in
[sig/v1.6/sha256sum.txt](https://raw.githubusercontent.com/stedolan/jq/master/sig/v1.6/sha256sum.txt).
The checksums for jq 1.5 are in
[sig/v1.5/sha256sum.txt](https://raw.githubusercontent.com/stedolan/jq/master/sig/v1.5/sha256sum.txt).
@@ -113,6 +131,8 @@ body:
[jq Package Signing Key](https://raw.githubusercontent.com/stedolan/jq/master/sig/jq-release.key).
The signatures can be found under
[sig/v1.x/\*.asc](https://github.com/stedolan/jq/tree/master/sig).
The signatures for jq 1.6 are in
[sig/v1.5/\*.asc](https://github.com/stedolan/jq/tree/master/sig/v1.6).
The signatures for jq 1.5 are in
[sig/v1.5/\*.asc](https://github.com/stedolan/jq/tree/master/sig/v1.5).
You can use [GnuPG](https://gnupg.org/) to verify a signature by downloading
@@ -120,6 +140,7 @@ body:
### From source on Linux, OS X, Cygwin, and other POSIX-like operating systems
* [Source tarball for jq 1.6](https://github.com/stedolan/jq/releases/download/jq-1.6/jq-1.6.tar.gz)
* [Source tarball for jq 1.5](https://github.com/stedolan/jq/releases/download/jq-1.5/jq-1.5.tar.gz)
You can build it using the usual `./configure && make && sudo
@@ -159,14 +180,13 @@ body:
#### Building the documentation
jq's documentation is compiled into static HTML using
[Bonsai](http://www.tinytree.info). To view the documentation
locally, run `rake serve` (or `bundle exec rake serve`) from the
docs/ subdirectory. To build the docs just `rake build` from the
docs subdirectory. You'll need a few Ruby dependencies, which can
be installed by following the instructions in `docs/README.md`.
jq's documentation is compiled into static HTML using Python.
To build the docs, run `pipenv run python3 build_website.py` from
the docs/ subdirectory. To serve them locally, you can run
`python3 -m SimpleHTTPServer`. You'll need a few Python dependencies,
which can be installed by following the instructions in `docs/README.md`.
The man page is built by `make jq.1`, or just `make`, also from
the YAML docs, and you'll still need the Ruby dependencies to
the YAML docs, and you'll still need the Python dependencies to
build the manpage.
@@ -33,6 +33,12 @@ tail: |
[Freenode](https://webchat.freenode.net/).
news:
- date: 1 November 2018
body: |
jq 1.6 released. See installation options on the [download](/jq/download/)
page, and the [release notes](https://github.com/stedolan/jq/releases/tag/jq-1.6)
for details.
- date: 15 August 2015
body: |
File diff suppressed because it is too large. Load diff
@@ -441,7 +441,7 @@ sections:
input: 5
output: [6]
- title: `length`
- title: '`length`'
body: |
The builtin function `length` gets the length of various
@@ -462,7 +462,7 @@ sections:
input: '[[1,2], "string", {"a":2}, null]'
output: [2, 6, 1, 0]
- title: `keys`
- title: '`keys`'
body: |
The builtin function `keys`, when given an object, returns
@@ -485,7 +485,7 @@ sections:
input: '[42,3,35]'
output: ['[0,1,2]']
- title: `has`
- title: '`has`'
body: |
The builtin function `has` returns whether the input object
@@ -504,7 +504,7 @@ sections:
input: '[[0,1], ["a","b","c"]]'
output: ['[false, true]']
- title: `to_entries`, `from_entries`, `with_entries`
- title: '`to_entries`, `from_entries`, `with_entries`'
body: |
These functions convert between an object and an array of
@@ -529,14 +529,14 @@ sections:
output: ['{"KEY_a": 1, "KEY_b": 2}']
- title: `select`
- title: '`select`'
body: |
The function `select(foo)` produces its input unchanged if
`foo` returns true for that input, and produces no output
otherwise.
It's useful for filtering lists: `[1,2,3] | map(select(. >= 2))`
It's useful for filtering lists: '`[1,2,3] | map(select(. >= 2))`'
will give you `[3]`.
examples:
@@ -544,7 +544,7 @@ sections:
input: '[1,5,3,0,7]'
output: ['[5,3,7]']
- title: `empty`
- title: '`empty`'
body: |
`empty` returns no results. None at all. Not even `null`.
@@ -559,7 +559,7 @@ sections:
input: 'null'
output: ['[1,2,3]']
- title: `map(x)`
- title: '`map(x)`'
body: |
For any filter `x`, `map(x)` will run that filter for each
@@ -574,7 +574,7 @@ sections:
input: '[1,2,3]'
output: ['[2,3,4]']
- title: `add`
- title: '`add`'
body: |
The filter `add` takes as input an array, and produces as
@@ -596,7 +596,7 @@ sections:
input: '[]'
output: ["null"]
- title: `range`
- title: '`range`'
body: |
The `range` function produces a range of numbers. `range(4;10)`
@@ -612,7 +612,7 @@ sections:
input: 'null'
output: ['[2,3]']
- title: `tonumber`
- title: '`tonumber`'
body: |
The `tonumber` function parses its input as a number. It
@@ -624,7 +624,7 @@ sections:
input: '[1, "1"]'
output: [1, 1]
- title: `tostring`
- title: '`tostring`'
body: |
The `tostring` function prints its input as a
@@ -636,7 +636,7 @@ sections:
input: '[1, "1", [1]]'
output: ['"1"', '"1"', '"[1]"']
- title: `type`
- title: '`type`'
body: |
The `type` function returns the type of its argument as a
@@ -648,7 +648,7 @@ sections:
input: '[0, false, [], {}, null, "hello"]'
output: ['["number", "boolean", "array", "object", "null", "string"]']
- title: `sort, sort_by`
- title: '`sort, sort_by`'
body: |
The `sort` functions sorts its input, which must be an
@@ -680,7 +680,7 @@ sections:
input: '[{"foo":4, "bar":10}, {"foo":3, "bar":100}, {"foo":2, "bar":1}]'
output: ['[{"foo":2, "bar":1}, {"foo":3, "bar":100}, {"foo":4, "bar":10}]']
- title: `group_by`
- title: '`group_by`'
body: |
`group_by(.foo)` takes as input an array, groups the
@@ -697,7 +697,7 @@ sections:
input: '[{"foo":1, "bar":10}, {"foo":3, "bar":100}, {"foo":1, "bar":1}]'
output: ['[[{"foo":1, "bar":10}, {"foo":1, "bar":1}], [{"foo":3, "bar":100}]]']
- title: `min`, `max`, `min_by`, `max_by`
- title: '`min`, `max`, `min_by`, `max_by`'
body: |
Find the minimum or maximum element of the input array. The
@@ -713,7 +713,7 @@ sections:
input: '[{"foo":1, "bar":14}, {"foo":2, "bar":3}]'
output: ['{"foo":2, "bar":3}']
- title: `unique`
- title: '`unique`'
body: |
The `unique` function takes as input an array and produces
@@ -725,7 +725,7 @@ sections:
input: '[1,2,5,3,5,3,1,3]'
output: ['[1,2,3,5]']
- title: `reverse`
- title: '`reverse`'
body: |
This function reverses an array.
@@ -735,7 +735,7 @@ sections:
input: '[1,2,3,4]'
output: ['[4,3,2,1]']
- title: `contains`
- title: '`contains`'
body: |
The filter `contains(b)` will produce true if b is
@@ -764,7 +764,7 @@ sections:
input: '{"foo": 12, "bar":[1,2,{"barp":12, "blip":13}]}'
output: ['false']
- title: `recurse`
- title: '`recurse`'
body: |
The `recurse` function allows you to search through a
@@ -796,7 +796,7 @@ sections:
- '{"foo":[]}'
- title: "String interpolation - `\(foo)`"
- title: String interpolation - `\(foo)`
body: |
Inside a string, you can put an expression inside parens
@@ -882,7 +882,7 @@ sections:
- title: Conditionals and Comparisons
entries:
- title: `==`, `!=`
- title: '`==`, `!=`'
body: |
The expression 'a == b' will produce 'true' if the result of a and b
@@ -931,7 +931,7 @@ sections:
input: 2
output: ['"many"']
- title: `>, >=, <=, <`
- title: '`>, >=, <=, <`'
body: |
The comparison operators `>`, `>=`, `<=`, `<` return whether
File renamed without changes.
@@ -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,
File diff suppressed because it is too large. Load diff
File renamed without changes.
+173
View File
@@ -0,0 +1,173 @@
body {
padding-top: 80px;
}
.container {
max-width: 970px;
}
/* index.liquid *******************************************/
#blurb {
padding-top: 40px;
}
#blurb p {
font-size: 1.9em;
}
#blurb .btn-group {
margin: 4px;
}
#multiblurb {
line-height: 1.7;
text-align: center;
font-size: 12pt;
}
#multiblurb code {
border: 0;
font-size: 12pt;
}
#news {
font-size: 12pt;
}
#news .date {
font-style: italic;
}
/* default.liquid *****************************************/
.tutorial-example {
position: relative;
margin-bottom: 10px;
}
.tutorial-example pre {
margin-bottom: 0px;
}
.tutorial-example a {
position: absolute;
top: 0px;
right: 0px;
padding: 15px 8px;
color: #777777;
font-weight: bold;
line-height: 10px;
font-size: 12px;
border-left: 1px solid #DDDDDD;
display: block;
}
.tutorial-example .accordion-body pre {
margin: 0 4px;
border-top: 0;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
@media print {
.tutorial-example a {
display: none;
}
}
/* manual.liquid ******************************************/
section {
padding-top: 24px;
}
h3 code {
border: 0;
font-size: 20px;
}
@media (max-width: 991px) {
#navcolumn {
/* Put nav column above manual content */
position: relative !important;
margin-bottom: 60px;
}
}
@media (min-width: 992px) {
#manualcontent {
/* Put nav column left of manual content */
padding-left: 280px;
}
}
.nav-pills {
margin-bottom: 20px;
}
.nav-pills li a {
padding: 8px 12px;
}
.manual-example table {
border-top: 1px solid #E5E5E5;
}
.manual-example table td {
white-space: pre-wrap;
font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
}
.manual-example table td.jqprogram {
font-weight: bold;
}
.manual-example table th {
text-align: right;
padding-right: 10px;
}
@media print {
#navcolumn {
display: none !important;
}
.manual-example {
display: block !important;
height: auto !important;
}
.jqplay-btn {
display: none !important;
}
}
/* shared/_footer.liquid **********************************/
footer {
background-color: #F5F5F5;
padding: 20px 0;
margin-top: 40px;
color: #999999;
text-align: center;
}
footer p {
margin: 8px 0;
}
/* typeahead **********************************************/
.twitter-typeahead {
width: 100%;
}
.tt-menu {
width: 100%;
background-color: #fff;
padding: 8px 0;
border: 1px solid #ccc;
border: 1px solid rgba(0, 0, 0, 0.2);
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
border-radius: 8px;
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
-moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
}
.tt-suggestion {
padding: 3px 20px;
}
.tt-suggestion:hover {
cursor: pointer;
color: #fff;
background-color: #446e9b;
}
.tt-suggestion.tt-cursor {
color: #fff;
background-color: #446e9b;
}
.tt-suggestion p {
margin: 0;
}
+1 -1
View File
@@ -44,7 +44,7 @@ $(function() {
$.each($('.manual-example table'), function(index, value) {
$value = $(value)
var j = $value.find('tr:nth-child(2) td:first').text();
var q = $value.find('.jqprogram').text().replace(/^jq /, '').replace(/^'(.+)'$/, '$1');
var q = $value.find('.jqprogram').text().replace(/^jq /, '').replace(/(\r\n|\n|\r)/gm," ").replace(/^'(.+)'$/, '$1');
var url = 'https://jqplay.org/jq?q=' + encodeURIComponent(q) +'&j=' + encodeURIComponent(j)
var $last_tr = $value.find('tr:last');
$last_tr.after('<tr class="jqplay-btn"><th><a href="' + url + '" class="btn btn-primary btn-sm">Run</a></th><th></th></tr><tr><th></th><th></th></tr>');
@@ -1,26 +1,26 @@
<!DOCTYPE html>
<html lang="en">
{% include "shared/head" %}
{% include "shared/_head.html.j2" %}
<body id="{{slug}}">
{% include "shared/navbar" %}
{% include "shared/_navbar.html.j2" %}
<div class="container">
<div class="row">
<h1>{{headline}}</h1>
{% for item in body %}
{% if item.text %}
{{ item.text | replace: '$JQ_VERSION', jq_version | markdownify }}
{{ item.text | replace('$JQ_VERSION', jq_version) | markdownify }}
{% endif %}
{% if item.command %}
{% capture resultID %}{{ "result" | unique}}{% endcapture %}
{% set resultID = unique_id() %}
<div class="tutorial-example">
<div class="accordion-heading">
<pre>{{item.command}}</pre>
<a data-toggle="collapse" href="#{{resultID}}">Show result</a>
<a data-toggle="collapse" href="#result{{resultID}}">Show result</a>
</div>
<div id="{{resultID}}" class="accordion-body collapse">
<div id="result{{resultID}}" class="accordion-body collapse">
<pre>{{item.result}}</pre>
</div>
</div>
@@ -29,6 +29,6 @@
</div>
</div>
{% include "shared/footer" %}
{% include "shared/_footer.html.j2" %}
</body>
</html>
@@ -1,9 +1,9 @@
<!DOCTYPE html>
<html lang="en">
{% include "shared/head" %}
{% include "shared/_head.html.j2" %}
<body id="{{slug}}">
{% include "shared/navbar" %}
<body id="{{ slug }}">
{% include "shared/_navbar.html.j2" %}
<div class="container">
<div class="row">
@@ -20,14 +20,14 @@
<div class="btn-toolbar" role="toolbar">
<div class="btn-group" role="group">
<a class="btn btn-primary dropdown-toggle" data-toggle="dropdown" href="#">
Download jq 1.5
Download jq 1.6
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li><a href="https://github.com/stedolan/jq/releases/download/jq-1.5/jq-linux64">Linux (64-bit)</a></li>
<li><a href="https://github.com/stedolan/jq/releases/download/jq-1.5/jq-osx-amd64">OS X (64-bit)</a></li>
<li><a href="https://github.com/stedolan/jq/releases/download/jq-1.5/jq-win64.exe">Windows (64-bit)</a></li>
<li><a href="/jq/download/">Other platforms, older versions, and source</a></li>
<li><a href="https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64">Linux (64-bit)</a></li>
<li><a href="https://github.com/stedolan/jq/releases/download/jq-1.6/jq-osx-amd64">OS X (64-bit)</a></li>
<li><a href="https://github.com/stedolan/jq/releases/download/jq-1.6/jq-win64.exe">Windows (64-bit)</a></li>
<li><a href="{{root}}/download/">Other platforms, older versions, and source</a></li>
</ul>
</div>
@@ -68,6 +68,6 @@
</div>
</div>
{% include "shared/footer" %}
{% include "shared/_footer.html.j2" %}
</body>
</html>
@@ -1,9 +1,9 @@
<!DOCTYPE html>
<html lang="en">
{% include "shared/head" %}
{% include "shared/_head.html.j2" %}
<body id="{{slug}}" data-spy="scroll" data-target="#navcolumn" data-offset="100">
{% include "shared/navbar" %}
{% include "shared/_navbar.html.j2" %}
<div class="container">
<div class="row">
@@ -32,41 +32,41 @@
{% for section in sections %}
<section id="{{section.title | section_id}}">
<h2>{{section.title}}</h2>
{{section.body | markdownify}}
{{ (section.body | markdownify) if section.body }}
{% for entry in section.entries %}
<section id="{{entry.title | entry_id}}">
<h3>
{{entry.title | markdownify | no_paragraph}}
{{entry.title | markdownify | no_paragraph }}
{% if entry.subtitle %}<small>{{entry.subtitle}}</small>{% endif %}
</h3>
{{entry.body | markdownify}}
{% if entry.examples %}
<div>
{% capture exampleID %}{{ "" | unique }}{% endcapture %}
{% set exampleID = unique_id() %}
<a data-toggle="collapse" href="#example{{exampleID}}">
<i class="glyphicon glyphicon-chevron-right"></i>
{% if entry.examples[1] %}Examples{%else%}Example{%endif%}
{% if entry.examples | length > 1 %}Examples{%else%}Example{%endif%}
</a>
<div id="example{{exampleID}}" class="manual-example collapse">
{% for example in entry.examples %}
<table>
<tr><th></th><td class="jqprogram">jq '{{example.program | escape}}'</td></tr>
<tr><th>Input</th><td>{{example.input | escape}}</td></tr>
{% unless example.output[0] %}
<tr><th></th><td class="jqprogram">jq '{{ example.program }}'</td></tr>
<tr><th>Input</th><td>{{ example.input }}</td></tr>
{% if not example.output[0] %}
<tr>
<th>Output</th>
<td><i>none</i></td>
</tr>
{% endunless %}
{% endif %}
{% for output in example.output %}
<tr>
{% if forloop.index == 1 %}
{% if loop.first %}
<th>Output</th>
{% else %}
<th></th>
{% endif %}
<td>{{output | escape}}</td>
<td>{{ output }}</td>
</tr>
{% endfor %}
</table>
@@ -82,15 +82,15 @@
</div>
</div>
{% include "shared/footer" %}
{% include "shared/_footer.html.j2" %}
<script>
var section_map = {
{% for section in sections %}
{% for entry in section.entries %}
{{entry.title | search_id | json}} : {{entry.title | entry_id | json}},
{{entry.title | search_id | tojson }} : {{entry.title | entry_id | tojson }},
{% endfor %}
{{section.title | search_id | json}} : {{section.title | section_id | json}}
{% unless forloop.last %},{% endunless %}
{{section.title | search_id | tojson }} : {{section.title | section_id | tojson }}
{% if not loop.last %},{% endif %}
{% endfor %}
};
</script>
@@ -1,6 +1,6 @@
<footer>
<div class="container">
<p>This website is made with <a href="http://www.tinytree.info">Bonsai</a> and <a href="http://getbootstrap.com">Bootstrap</a>, themed with <a href="https://bootswatch.com">Bootswatch</a>.</p>
<p>This website is made with <a href="http://getbootstrap.com">Bootstrap</a>, themed with <a href="https://bootswatch.com">Bootswatch</a>.</p>
<p>jq is licensed under the MIT license (code) and the <a href="https://creativecommons.org/licenses/by/3.0/">CC-BY-3.0</a> license (docs).</p>
</div>
</footer>
@@ -7,9 +7,9 @@
<title>{{headline}}</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<link rel="canonical" href="{{url}}{{permalink | remove:'/index'}}" />
<link rel="canonical" href="{{ url }}{{ permalink | replace('/index','') }}" />
<link href="https://maxcdn.bootstrapcdn.com/bootswatch/3.3.5/spacelab/bootstrap.min.css" rel="stylesheet" integrity="sha256-j7Dtnd7ZjexEiPNbscbopFn9+Cs0b3TLipKsWAPHZIM= sha512-RFhfi6P8zWMAJrEGU+CPjuxPh3r/UUBGqQ+/o6WKPIVZmQqeOipGotH2ihRULuQ8wsMBoK15TSZqc/7VYWyuIw==" crossorigin="anonymous">
<link rel="stylesheet" href="{{root}}/css/base.css" type="text/css">
<link rel="stylesheet" href="{{ root }}/css/base.css" type="text/css">
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
@@ -13,8 +13,8 @@
<div class="navbar-collapse collapse" id="nav-collapse">
<ul class="nav navbar-nav">
{% for item in navigation %}
<li {% if item.permalink == permalink %} class="active" {% endif %}>
<a href="{{root}}{{item.permalink}}">{{item.name}}</a>
<li {% if item == permalink %} class="active" {% endif %}>
<a href="{{root}}/{{item}}">{{item | capitalize}}</a>
</li>
{% endfor %}
<li><a href="https://github.com/stedolan/jq/issues">Issues</a></li>
+526 -142
View File
File diff suppressed because it is too large. Load diff
+1
Submodule modules/oniguruma added at 6fa38f4084.
+1
View File
@@ -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
-1
View File
@@ -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]) +
+1 -1
View File
@@ -11,7 +11,7 @@ set -o xtrace
scriptdir=`dirname "$0"`
cd "$scriptdir"/../docs
rm -rf output
rake build
pipenv run python3 build_website.py
cd ..
# copy to /tmp
+7 -2
View File
@@ -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
+16
View File
@@ -0,0 +1,16 @@
-----BEGIN PGP SIGNATURE-----
iQIzBAABCAAdFiEET9cB1vqbPS31rJNdrxkEDHFSNAIFAlvbqFAACgkQrxkEDHFS
NAIQNRAArTVuw4JwSbxKzC4Y+ib1Vaz5d8ntQwmiR1FBJ5hbi5dMhqD81Kl4slU0
0IDBia7Bnvthk0/H+UZHjUdkCNW8rubg87zJOt8t5XXQmIK81cPgKhxqhPA6JPA8
RNmBGs5gAxycm8x4X0EMOVTsVqAfq8NYakO6MctRlv4ut3lXfWe+prIMBpFQZr7l
CqvmBY8xhLF09LLBEB+RTjhDeW7E02cYFJIW9bbcvpECmnX/fJTjGwN6+Blaq4ln
nJwODDZ61TGC2ZAYcAoqcA8bkU8ms0x7RCpZrRbYG4HJstSYM8RaxUxvtW2zn6Si
7FpMvrXnDn6MWr3wph96oRTEkKQsRhxoxo7zumaiKkaAGKjyN7fFx9z89QrKoxrO
CAzGdHjt6YSmXH60ArGUWKGRilyvxieKddXD3fMJ5Sz/u+yHFRNDIuOQt+KZaOfC
UlvKXQamexkXmK3PEl5ZPxcuK+G/NLFawPlLLHVvdLx6B5o4r94ycfLLYqzDCiby
v0EDZrgVropUMUkJJMjSmioXoizK9KZ/fl5fj+2miHEZS+s+YwZ5aoPi5IvrFUhi
sYz3wEKHp3aYzTPhPt3wBU9aaWoTradSCP4pt4eJV/VeVND+vygILdoD6xFWJoc4
BT2tBrUR6uqdQXaCn3BF0lCfI17fab1elYwBkh291wZeKF7qQ8Y=
=cTl4
-----END PGP SIGNATURE-----
+16
View File
@@ -0,0 +1,16 @@
-----BEGIN PGP SIGNATURE-----
iQIzBAABCAAdFiEET9cB1vqbPS31rJNdrxkEDHFSNAIFAlvbqF8ACgkQrxkEDHFS
NAK+UhAAx+mFwf7XRYLznTU4HoDzW3yn/H+amvYpphvtcn/eB0GsZRpZ3+EIkMUe
VF9Qxidw39tZeLROdX84iaYSu1bTpd6vAdx54rMUZyPVry6m5hqUD78LccPvIDCk
EyFuRuAAqlRicyNuqtlr9BUBoI7Z2c4YOA3Y4oXlOXtleL3SXKXcGvhcWQ4owZp5
MN6tT/lNu6sj8l5v0Y4GxtfDg8YOap2s9MdaNNkAgt3O3J98gadAin+vQ34ekKWr
zsB7QjZaAr8OSGyWCY7BTc7UJbsTIzFzSPvYGeIP2iCBSpR+qcM5/bgrE+Q4l2TA
t+p6vF1UVo9MkhEhD/2Ru/ovjqRKIVKA0J5YvEzr2jWjSAWpc75y80rovqW/vjE4
GHBJootozgrUwe/ESTATzdyfFQG8Du8znnQp1Wv3upRPieCKiLiUkRZenOTvBIvW
rHhgBR73vfhyD92exum4Z97hglwzjWJZRYohyKz+D0tYffH5lG4DV2pToREMqslq
9Yr3eGREMD/6j/zIsfzKxFeC/Ar+vmZrate9bUPwX7WYdwwbRcmx7zI6hIszFDnc
RONpuJokLxcgfVC3Q8Tzo5cRsAJ0eiQwO/K3oU/ZSAjJkV17E+cy3KFm/C0BSvXx
BHxK2D3vqCsbtlfkLurxzjvFKIAQtFO3yfSI5mIMnWfRCgvoHcU=
=O93f
-----END PGP SIGNATURE-----
+16
View File
@@ -0,0 +1,16 @@
-----BEGIN PGP SIGNATURE-----
iQIzBAABCAAdFiEET9cB1vqbPS31rJNdrxkEDHFSNAIFAlvbqGUACgkQrxkEDHFS
NAJT0hAAvuHiFUoXoHh6fJ16dY+ZvY4lWqAUap+mAWABuMpWGT0nfjq30eqvCY0e
Vjk46ENJPNjCNQnxMRq3kWMqsaniWLgU/sbXK1Ws7RldhJbxjnL8y/jHtNHQtrKV
+RhcJ3FhDzLSHOnIsTYYzHFZ1D+qeU+XMafVnnZSCj4SjEMZjzo9VyHJqyu+iG0w
z2spLd6zFp1+6EtZkdv10NsiB69HhNxKUn0BUE2KOfasLpFZSoMN2OXNx9qatTJ0
l/+PKzSb8DkRh3uHtowyV7VHrPUQ8/WcyRqwNKMmJaB3WDDwsPJxgQT3sJVpVQaj
4d768B23NbR3LZbqPgczu50kn/6Q/DJTm7J/TNxKIOfnCsIMsiIhQ+oLe7/8c0ph
xXhNZqzi06jB0kulmGus/YQY+Nojquzc+mMOysoxYo1iWscxWoK3GrImO3YMACk5
0zH6pHgLBlWZ/5ScKmvAn8tI/EfSUnsR/QMmFEZsc/80tkZBAJd3HPeamRtLqCon
T8aSP+xwwzo1BFhtdVddYXlXFWIwtn9VhEYJvXN12eyhW+S+dKyUlJCdCAozkntD
QxOILIc8RF4F3KvQTo+t91hbjyPGm760/iQgHU20UpRxaIkJylQ6p6rWsV1wEMMr
QEENmAs0IyawgJGJ1Jr7AKOR1fqEqW2BJxQ9zi9re+dXOvVWMI0=
=J3nx
-----END PGP SIGNATURE-----
+16
View File
@@ -0,0 +1,16 @@
-----BEGIN PGP SIGNATURE-----
iQIzBAABCAAdFiEET9cB1vqbPS31rJNdrxkEDHFSNAIFAlvbqGgACgkQrxkEDHFS
NAKFsQ//fWWzw7pWIt2v0vCn3VOAqWv0oB3+YEhqmYNv3UJ5cJBeOndR4C0kT2uH
MnTIR5WCLw9iSiNkHUX+oiWT3Rw8i8oHkA1Ub8sIin1RksV0TosTQa++9XqdO92q
HBBLUe1Y9BcIU0MgEKDtLXKwg6wqW6zAl0WI5/fIPT8Du7evhI32s9zVTMH4AahO
ZUM+OXmNCRDVvVp5LS3fZR8E+R3+Mt2ZplGb+aLmoX+hvVTc15GfVtFLGMz2bF1p
LVG2/qs9XQP9nCaPm/JCB1F04nHTAuUb5BzJtdJUpqBJGN8diQrxLigjyxKsPVNw
118r4Q/6FELTlMg9+6qOPal++ihjuCNWex5Mqz2+ZnANvtRYVx/5bF+DY848JmqP
V2k0JF/pRnwaCd9ONzYjkWcCMeoAAvwPygl20NFBUgTHC/xdh/0j+glG0IUW7Wt8
2cJyG2OpGqR8CHTc0WDwV1SM937B9CuPjYANottrl2mDmdhhykO5gpDSjf7/is4y
9sWr4MqI62aI8xBVdLmTsF4kzoWfC5go4SAHvXlsBr7bMW90hkcuuY0giB0mWZng
VQtRtpddKmhstLGoKMWf7TblX4uXA5Ym03LzvwhGl3PlwKh4HvkVYBE7EaSKuJ1g
s3WxN0vP6mP1nMFF26fC9IapYWIcTXjyJ7Hmo0Okwn543E37cQk=
=KfqY
-----END PGP SIGNATURE-----
+16
View File
@@ -0,0 +1,16 @@
-----BEGIN PGP SIGNATURE-----
iQIzBAABCAAdFiEET9cB1vqbPS31rJNdrxkEDHFSNAIFAlvbqGsACgkQrxkEDHFS
NAK2Sg/+O0nHwVg185EwKoORwL7TQ9V8VSV04Ska0lKJ0Biu0w0U8k+bkzhY5IZ5
IdNUZb5lhnPpLosCLlzHlWD3fNkBbpYLgxDrGDS7EujXcp8lI1WrwVNuY2Gbz7c3
09YAeF2ENnutS4KP0BUfdEVMlEqQIVpGldl5jKgHxlZ8nP80z6IpNL9FnX/qwkZu
/fyRthz1XAEcDHfzBtSbJCJ58QoGwl4S96TsuLZnnBuNgNk97SIeiDzg7tN1FmF2
wyg7mPZhIdRaGfgRLCqO7+qFGiYS4esJj4SbLiWDREVKhqqnT5B/vF5SmshsssUm
6MH+eK0BAGIntTs0fIGtgkiiuZI4bJpkxgfr5P6/F85U2d1YAJSyJubVMJhxx1U3
vKjUYzj4ub9GieT80FFmNAbuUTOFY3y5ZLDl3WRXD23/FpXTxWvTMNWs/Ypt67iT
SLcpC1iAETTsaAzLnPxPtwjApFJv/+Wy+CXZl1xAwDHH4vnSddRaSjjl9qsi7yNh
U4KUAVzpHfgmsK0DY1P4wXbBEuXCVcUBhGIQ7z5/Bw0F943C1+tFz7MckQmbsvay
O3+D8NeQGNb0wCIt/Ro/xIqSb8yrI4tL3+phnUxiZGxc0z6nTw1O9BdMRX6l5/5O
84QzUal8MoDWTy0pb1eqUaXp6Cek4c9Lqvc8WPq/csAE2jb/PGM=
=xykx
-----END PGP SIGNATURE-----
+5
View File
@@ -0,0 +1,5 @@
319af6123aaccb174f768a1a89fb586d471e891ba217fe518f81ef05af51edd9 jq-linux32
af986793a515d500ab2d35f8d2aecd656e764504b789b66d7e1a0b727a124c44 jq-linux64
5c0a0a3ea600f302ee458b30317425dd9632d1ad8882259fcaf4e9b868b2b1ef jq-osx-amd64
0012cb4c0eb6eaf97b842e676e423a69a8fea95055d93830551b4a5a54494bd8 jq-win32.exe
a51d36968dcbdeabb3142c6f5cf9b401a65dc3a095f3144bd0c118d5bb192753 jq-win64.exe
+491 -114
View File
@@ -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,11 +29,14 @@ 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>
#include <time.h>
#ifdef WIN32
#include <windows.h>
#endif
#include "builtin.h"
#include "compile.h"
#include "jq_parser.h"
@@ -36,6 +44,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 +70,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 +116,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 +211,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 +227,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 +465,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 +635,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 +646,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 +654,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 +747,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;
@@ -716,7 +865,7 @@ static jv f_match(jq_state *jq, jv input, jv regex, jv modifiers, jv testmode) {
if (region->end[0] == region->beg[0]) {
unsigned long idx;
const char *fr = (const char*)input_string;
for (idx = 0; fr != input_string+region->beg[0]; idx++) {
for (idx = 0; fr < input_string+region->beg[0]; idx++) {
fr += jvp_utf8_decode_length(*fr);
}
jv match = jv_object_set(jv_object(), jv_string("offset"), jv_number(idx));
@@ -753,7 +902,7 @@ static jv f_match(jq_state *jq, jv input, jv regex, jv modifiers, jv testmode) {
cap = jv_object_set(cap, jv_string("string"), jv_null());
} else {
fr = input_string;
for (idx = 0; fr != input_string+region->beg[i]; idx++) {
for (idx = 0; fr < input_string+region->beg[i]; idx++) {
fr += jvp_utf8_decode_length(*fr);
}
cap = jv_object_set(jv_object(), jv_string("offset"), jv_number(idx));
@@ -765,7 +914,7 @@ static jv f_match(jq_state *jq, jv input, jv regex, jv modifiers, jv testmode) {
continue;
}
fr = input_string;
for (idx = len = 0; fr != input_string+region->end[i]; len++) {
for (idx = len = 0; fr < input_string+region->end[i]; len++) {
if (fr == input_string+region->beg[i]) idx = len, len=0;
fr += jvp_utf8_decode_length(*fr);
}
@@ -786,7 +935,7 @@ static jv f_match(jq_state *jq, jv input, jv regex, jv modifiers, jv testmode) {
break;
} else { /* Error */
UChar ebuf[ONIG_MAX_ERROR_MESSAGE_LEN];
onig_error_code_to_str(ebuf, onigret, einfo);
onig_error_code_to_str(ebuf, onigret, &einfo);
jv_free(result);
result = jv_invalid_with_msg(jv_string_concat(jv_string("Regex failure: "),
jv_string((char*)ebuf)));
@@ -802,11 +951,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)
@@ -907,21 +1056,20 @@ static jv f_nan(jq_state *jq, jv input) {
return jv_number(NAN);
}
static jv f_error(jq_state *jq, jv input, jv msg) {
jv_free(input);
return jv_invalid_with_msg(msg);
static jv f_error(jq_state *jq, jv input) {
return jv_invalid_with_msg(input);
}
// 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 +1080,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 +1112,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 +1130,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 +1174,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;
}
@@ -1028,6 +1188,27 @@ static jv tm2jv(struct tm *tm) {
jv_number(tm->tm_yday));
}
#if defined(WIN32) && !defined(HAVE_SETENV)
static int setenv(const char *var, const char *val, int ovr)
{
BOOL b;
char c[2];
if (!ovr)
{
DWORD d;
d = GetEnvironmentVariableA (var, c, 2);
if (0 != d && GetLastError () != ERROR_ENVVAR_NOT_FOUND) {
return d;
}
}
b = SetEnvironmentVariableA (var, val);
if (b) {
return 0;
}
return 1;
}
#endif
/*
* mktime() has side-effects and anyways, returns time in the local
* timezone, not UTC. We want timegm(), which isn't standard.
@@ -1046,61 +1227,170 @@ static jv tm2jv(struct tm *tm) {
static time_t my_mktime(struct tm *tm) {
#ifdef HAVE_TIMEGM
return timegm(tm);
#else /* HAVE_TIMEGM */
#elif HAVE_TM_TM_GMT_OFF
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
time_t t = mktime(tm);
if (t == (time_t)-1)
return t;
return t + tm->__tm_gmtoff;
#elif WIN32
return _mkgmtime(tm);
#else
return (time_t)-2; /* Not supported */
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
#endif /* !HAVE_TIMEGM */
}
#ifdef HAVE_STRPTIME
/* 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;
}
#ifndef HAVE_STRPTIME
static char *strptime(const char *s, const char *format, struct tm *tm) {
if (strcmp(format, "%Y-%m-%dT%H:%M:%SZ"))
return NULL;
int count, end;
count = sscanf(s, "%d-%d-%dT%d:%d:%d%n",
&tm->tm_year, &tm->tm_mon, &tm->tm_mday,
&tm->tm_hour, &tm->tm_min, &tm->tm_sec,
&end );
if (count == 6 && s[end] == 'Z') {
tm->tm_year -= 1900;
tm->tm_mon--;
return (char*)s + end + 1;
}
return NULL;
}
#endif
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);
#ifndef HAVE_STRPTIME
if (strcmp(fmt, "%Y-%m-%dT%H:%M:%SZ")) {
return ret_error2(a, b, jv_string("strptime/1 only supports ISO 8601 on this platform"));
}
#endif
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));
jv_free(a); // must come after `*end` because `end` is a pointer into `a`'s string
return r;
}
#else
static jv f_strptime(jq_state *jq, jv a, jv b) {
jv_free(a);
jv_free(b);
return jv_invalid_with_msg(jv_string("strptime/1 not implemented on this platform"));
}
#endif
#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 +1423,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 +1440,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 +1455,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 +1474,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 +1535,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 +1587,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},
@@ -1294,27 +1670,33 @@ static const struct cfunction function_list[] = {
{(cfunction_ptr)f_max, "max", 1},
{(cfunction_ptr)f_min_by_impl, "_min_by_impl", 2},
{(cfunction_ptr)f_max_by_impl, "_max_by_impl", 2},
{(cfunction_ptr)f_error, "error", 2},
{(cfunction_ptr)f_error, "error", 1},
{(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},
{(cfunction_ptr)f_match, "_match_impl", 4},
{(cfunction_ptr)f_modulemeta, "modulemeta", 1},
{(cfunction_ptr)f_input, "_input", 1},
{(cfunction_ptr)f_input, "input", 1},
{(cfunction_ptr)f_debug, "debug", 1},
{(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,62 +1743,57 @@ 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(builtins, b);
}
#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"
static const char jq_builtins[] =
/* 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 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);
}
locfile_free(src);
return nerrors;
}
static int slurp_lib(jq_state *jq, block* bb) {
int nerrors = 0;
char* home = getenv("HOME");
if (home) { // silently ignore no $HOME
jv filename = jv_string_append_str(jv_string(home), "/.jq");
jv data = jv_load_file(jv_string_value(filename), 1);
if (jv_is_valid(data)) {
nerrors = builtins_bind_one(jq, bb, jv_string_value(data) );
}
jv_free(filename);
jv_free(data);
}
return nerrors;
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)));
}
int builtins_bind(jq_state *jq, block* bb) {
int nerrors = slurp_lib(jq, bb);
if (nerrors) {
block_free(*bb);
return nerrors;
}
nerrors = builtins_bind_one(jq, bb, jq_builtins);
block builtins;
struct locfile* src = locfile_init(jq, "<builtin>", jq_builtins, sizeof(jq_builtins)-1);
int nerrors = jq_parse_library(src, &builtins);
assert(!nerrors);
*bb = bind_bytecoded_builtins(*bb);
*bb = gen_cbinding(function_list, sizeof(function_list)/sizeof(function_list[0]), *bb);
locfile_free(src);
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_incremental(builtins, *bb, OP_IS_CALL_PSEUDO);
*bb = block_drop_unreferenced(*bb);
return nerrors;
}
+49 -25
View File
@@ -1,4 +1,5 @@
def error: error(.);
def halt_error: halt_error(5);
def error(msg): msg|error;
def map(f): [.[] | f];
def select(f): if f then . else empty end;
def sort_by(f): _sort_by_impl(map([f]));
@@ -9,12 +10,12 @@ def max_by(f): _max_by_impl(map([f]));
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 _assign(paths; $value): reduce path(paths) as $p (.; setpath($p; $value));
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 label $out | foreach exp as $item ($n; .-1; $item, if . <= 0 then break $out else empty end)
elif $n == 0 then empty
else exp 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];
@@ -195,12 +200,11 @@ def transpose:
end;
def in(xs): . as $x | xs | has($x);
def inside(xs): . as $x | xs | contains($x);
def input: _input;
def repeat(exp):
def _repeat:
exp, _repeat;
_repeat;
def inputs: try repeat(_input) catch if .=="break" then empty else .|error end;
def inputs: try repeat(input) catch if .=="break" then empty else .|error end;
# like ruby's downcase - only characters A to Z are affected
def ascii_downcase:
explode | map( if 65 <= . and . <= 90 then . + 32 else . end) | implode;
@@ -210,24 +214,27 @@ 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];
if ($item[0]|length) == 0 then [null,false,.[2],.[3]]
elif ($item|length) == 1 and ($item[0]|length) < 2 then [null,false,.[0],.[1]]
else . end |
. as $state |
if ($item|length) > 1 and ($item[0]|length) > 0 then
[.[0]|setpath(($item|.[0]); ($item|.[1])),
true,
$state[2],
$state[3]]
foreach i as $i (
[null, null];
if ($i | length) == 2 then
if ($i[0] | length) == 0 then .
else [ ( .[0] | setpath($i[0]; $i[1]) ), .[1] ]
end
elif ($i[0] | length) == 1 then [ null, .[0] ]
else .
end;
if ($item[0]|length) == 1 and ($item|length == 1) and .[3] then .[2] else empty end,
if ($item[0]|length) == 0 then $item[1] else empty end
);
if ($i | length) == 1 then
if ($i[0] | length) == 1 then .[1]
else empty
end
elif ($i[0] | length) == 0 then $i[1]
else empty
end
);
def tostream:
{string:true,number:true,boolean:true,null:true} as $leaf_types |
. as $dot |
@@ -235,7 +242,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 +289,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);
+310 -105
View File
@@ -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"
@@ -48,9 +49,11 @@ struct inst {
// Unbound instructions (references to other things that may or may not
// exist) are created by "gen_foo_unbound", and bindings are created by
// block_bind(definition, body), which binds all instructions in
// body which are unboudn and refer to "definition" by name.
// body which are unbound and refer to "definition" by name.
struct inst* bound_by;
char* symbol;
int any_unbound;
int referenced;
int nformals;
int nactuals;
@@ -72,6 +75,8 @@ static inst* inst_new(opcode op) {
i->bytecode_pos = -1;
i->bound_by = 0;
i->symbol = 0;
i->any_unbound = 0;
i->referenced = 0;
i->nformals = -1;
i->nactuals = -1;
i->subfn = gen_noop();
@@ -155,11 +160,19 @@ block gen_const_global(jv constant, const char *name) {
inst* i = inst_new(STORE_GLOBAL);
i->imm.constant = constant;
i->symbol = strdup(name);
i->any_unbound = 0;
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) {
@@ -203,22 +216,29 @@ block gen_op_unbound(opcode op, const char* name) {
assert(opcode_describe(op)->flags & OP_HAS_BINDING);
inst* i = inst_new(op);
i->symbol = strdup(name);
i->any_unbound = 1;
return inst_block(i);
}
block gen_op_var_fresh(opcode op, const char* name) {
assert(opcode_describe(op)->flags & OP_HAS_VARIABLE);
return block_bind(gen_op_unbound(op, name),
gen_noop(), OP_HAS_VARIABLE);
block b = gen_op_unbound(op, name);
b.first->bound_by = b.first;
return b;
}
block gen_op_bound(opcode op, block binder) {
assert(block_is_single(binder));
block b = gen_op_unbound(op, binder.first->symbol);
b.first->bound_by = binder.first;
b.first->any_unbound = 0;
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);
@@ -298,21 +318,7 @@ static int block_count_actuals(block b) {
return args;
}
static int block_count_refs(block binder, block body) {
int nrefs = 0;
for (inst* i = body.first; i; i = i->next) {
if (i != binder.first && i->bound_by == binder.first) {
nrefs++;
}
// counting recurses into closures
nrefs += block_count_refs(binder, i->subfn);
// counting recurses into argument list
nrefs += block_count_refs(binder, i->arglist);
}
return nrefs;
}
static int block_bind_subblock(block binder, block body, int bindflags, int break_distance) {
static int block_bind_subblock_inner(int* any_unbound, block binder, block body, int bindflags, int break_distance) {
assert(block_is_single(binder));
assert((opcode_describe(binder.first->op)->flags & bindflags) == (bindflags & ~OP_BIND_WILDCARD));
assert(binder.first->symbol);
@@ -324,6 +330,9 @@ static int block_bind_subblock(block binder, block body, int bindflags, int brea
binder.first->nformals = block_count_formals(binder);
int nrefs = 0;
for (inst* i = body.first; i; i = i->next) {
if (i->any_unbound == 0)
continue;
int flags = opcode_describe(i->op)->flags;
if ((flags & bindflags) == (bindflags & ~OP_BIND_WILDCARD) && i->bound_by == 0 &&
(!strcmp(i->symbol, binder.first->symbol) ||
@@ -345,14 +354,25 @@ static int block_bind_subblock(block binder, block body, int bindflags, int brea
// a break whenever we come across a STOREV of *anonlabel...
break_distance++;
}
i->any_unbound = (i->symbol && !i->bound_by);
// binding recurses into closures
nrefs += block_bind_subblock(binder, i->subfn, bindflags, break_distance);
nrefs += block_bind_subblock_inner(&i->any_unbound, binder, i->subfn, bindflags, break_distance);
// binding recurses into argument list
nrefs += block_bind_subblock(binder, i->arglist, bindflags, break_distance);
nrefs += block_bind_subblock_inner(&i->any_unbound, binder, i->arglist, bindflags, break_distance);
if (i->any_unbound)
*any_unbound = 1;
}
return nrefs;
}
static int block_bind_subblock(block binder, block body, int bindflags, int break_distance) {
int any_unbound;
return block_bind_subblock_inner(&any_unbound, binder, body, bindflags, break_distance);
}
static int block_bind_each(block binder, block body, int bindflags) {
assert(block_has_only_binders(binder, bindflags));
bindflags |= OP_HAS_BINDING;
@@ -363,7 +383,7 @@ static int block_bind_each(block binder, block body, int bindflags) {
return nrefs;
}
block block_bind(block binder, block body, int bindflags) {
static block block_bind(block binder, block body, int bindflags) {
block_bind_each(binder, body, bindflags);
return block_join(binder, body);
}
@@ -401,72 +421,101 @@ block block_bind_library(block binder, block body, int bindflags, const char *li
return body; // We don't return a join because we don't want those sticking around...
}
// Bind binder to body and throw away any defs in binder not referenced
// (directly or indirectly) from body.
// Bind binder to body, then throw it away if not referenced.
block block_bind_referenced(block binder, block body, int bindflags) {
assert(block_is_single(binder));
assert(block_has_only_binders(binder, bindflags));
bindflags |= OP_HAS_BINDING;
block refd = gen_noop();
block unrefd = gen_noop();
int nrefs;
for (int last_kept = 0, kept = 0; ; ) {
for (inst* curr; (curr = block_take(&binder));) {
block b = inst_block(curr);
nrefs = block_bind_each(b, body, bindflags);
// Check if this binder is referenced from any of the ones we
// already know are referenced by body.
nrefs += block_count_refs(b, refd);
nrefs += block_count_refs(b, body);
if (nrefs) {
refd = BLOCK(refd, b);
kept++;
} else {
unrefd = BLOCK(unrefd, b);
}
if (block_bind_subblock(binder, body, bindflags, 0) == 0) {
block_free(binder);
} else {
body = BLOCK(binder, body);
}
return body;
}
static inst* block_take_last(block* b) {
inst* i = b->last;
if (i == 0)
return 0;
if (i->prev) {
i->prev->next = i->next;
b->last = i->prev;
i->prev = 0;
} else {
b->first = 0;
b->last = 0;
}
return i;
}
// Binds a sequence of binders, which *must not* alrady be bound to each other,
// to body, throwing away unreferenced defs
block block_bind_incremental(block binder, block body, int bindflags) {
assert(block_has_only_binders(binder, bindflags));
bindflags |= OP_HAS_BINDING;
inst* curr;
while ((curr = block_take_last(&binder))) {
body = block_bind_referenced(inst_block(curr), body, bindflags);
}
return body;
}
block block_bind_self(block binder, int bindflags) {
assert(block_has_only_binders(binder, bindflags));
bindflags |= OP_HAS_BINDING;
block body = gen_noop();
inst* curr;
while ((curr = block_take_last(&binder))) {
block b = inst_block(curr);
block_bind_subblock(b, body, bindflags, 0);
body = BLOCK(b, body);
}
return body;
}
static void block_mark_referenced(block body) {
int saw_top = 0;
for (inst* i = body.last; i; i = i->prev) {
if (saw_top && i->bound_by == i && !i->referenced)
continue;
if (i->op == TOP) {
saw_top = 1;
}
if (kept == last_kept)
break;
last_kept = kept;
binder = unrefd;
unrefd = gen_noop();
if (i->bound_by) {
i->bound_by->referenced = 1;
}
block_mark_referenced(i->arglist);
block_mark_referenced(i->subfn);
}
block_free(unrefd);
return block_join(refd, body);
}
block block_drop_unreferenced(block body) {
inst* curr;
block_mark_referenced(body);
block refd = gen_noop();
block unrefd = gen_noop();
int drop;
do {
drop = 0;
while ((curr = block_take(&body)) && curr->op != TOP) {
block b = inst_block(curr);
if (block_count_refs(b,refd) + block_count_refs(b,body) == 0) {
unrefd = BLOCK(unrefd, b);
drop++;
} else {
refd = BLOCK(refd, b);
}
inst* curr;
while ((curr = block_take(&body))) {
if (curr->bound_by == curr && !curr->referenced) {
inst_free(curr);
} else {
refd = BLOCK(inst_block(curr), refd);
}
if (curr && curr->op == TOP) {
body = BLOCK(inst_block(curr),body);
}
body = BLOCK(refd, body);
refd = gen_noop();
} while (drop != 0);
block_free(unrefd);
return body;
}
return refd;
}
jv block_take_imports(block* body) {
jv imports = jv_array();
inst* top = NULL;
if (body->first && body->first->op == TOP) {
top = block_take(body);
}
/* Parser should never generate TOP before imports */
assert(!(body->first && body->first->op == TOP && body->first->next &&
(body->first->next->op == MODULEMETA || body->first->next->op == DEPS)));
while (body->first && (body->first->op == MODULEMETA || body->first->op == DEPS)) {
inst* dep = block_take(body);
if (dep->op == DEPS) {
@@ -474,12 +523,21 @@ jv block_take_imports(block* body) {
}
inst_free(dep);
}
if (top) {
*body = block_join(inst_block(top),*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);
@@ -526,6 +584,7 @@ block gen_function(const char* name, block formals, block body) {
}
i->subfn = body;
i->symbol = strdup(name);
i->any_unbound = -1;
i->arglist = formals;
block b = inst_block(i);
block_bind_subblock(b, b, OP_IS_CALL_PSEUDO | OP_HAS_BINDING, 0);
@@ -550,9 +609,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 +635,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 +660,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 +769,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 +874,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 +980,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 +1003,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 +1025,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 +1048,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)));
}
@@ -945,7 +1116,8 @@ block gen_cbinding(const struct cfunction* cfunctions, int ncfunctions, block co
inst* i = inst_new(CLOSURE_CREATE_C);
i->imm.cfunc = &cfunctions[cfunc];
i->symbol = strdup(i->imm.cfunc->name);
code = block_bind(inst_block(i), code, OP_IS_CALL_PSEUDO);
i->any_unbound = 0;
code = BLOCK(inst_block(i), code);
}
return code;
}
@@ -970,16 +1142,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 +1234,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 +1256,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 +1306,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 +1324,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 +1389,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);
+7 -2
View File
@@ -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);
@@ -69,14 +72,16 @@ int block_has_only_binders(block, int bindflags);
int block_has_main(block);
int block_is_funcdef(block b);
int block_is_single(block b);
block block_bind(block binder, block body, int bindflags);
block block_bind_library(block binder, block body, int bindflags, const char* libname);
block block_bind_referenced(block binder, block body, int bindflags);
block block_bind_incremental(block binder, block body, int bindflags);
block block_bind_self(block binder, 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);
+126 -23
View File
@@ -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,29 @@ 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
} else {
jv_free(jq->error);
}
jq->error = jv_null();
uint16_t offset = *pc++;
pc += offset;
@@ -932,6 +1000,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 +1046,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 +1134,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 +1164,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 +1231,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);
}
+12 -1
View File
@@ -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 */
+35 -9
View File
@@ -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;
}
}
/*
@@ -650,10 +658,12 @@ jv jv_string_indexes(jv j, jv k) {
int idxlen = jv_string_length_bytes(jv_copy(k));
jv a = jv_array();
p = jstr;
while ((p = _jq_memmem(p, (jstr + jlen) - p, idxstr, idxlen)) != NULL) {
a = jv_array_append(a, jv_number(p - jstr));
p += idxlen;
if (idxlen != 0) {
p = jstr;
while ((p = _jq_memmem(p, (jstr + jlen) - p, idxstr, idxlen)) != NULL) {
a = jv_array_append(a, jv_number(p - jstr));
p += idxlen;
}
}
jv_free(j);
jv_free(k);
@@ -1106,6 +1116,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);
@@ -1324,7 +1344,13 @@ int jv_contains(jv a, jv b) {
} else if (jv_get_kind(a) == JV_KIND_ARRAY) {
r = jv_array_contains(jv_copy(a), jv_copy(b));
} else if (jv_get_kind(a) == JV_KIND_STRING) {
r = strstr(jv_string_value(a), jv_string_value(b)) != 0;
int b_len = jv_string_length_bytes(jv_copy(b));
if (b_len != 0) {
r = _jq_memmem(jv_string_value(a), jv_string_length_bytes(jv_copy(a)),
jv_string_value(b), b_len) != 0;
} else {
r = 1;
}
} else {
r = jv_equal(jv_copy(a), jv_copy(b));
}
+11 -1
View File
@@ -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()))
@@ -188,7 +198,7 @@ int jv_get_refcnt(jv);
enum jv_print_flags {
JV_PRINT_PRETTY = 1,
JV_PRINT_ASCII = 2,
JV_PRINT_COLOR = 4,
JV_PRINT_COLOR = 4, JV_PRINT_COLOUR = 4,
JV_PRINT_SORTED = 8,
JV_PRINT_INVALID = 16,
JV_PRINT_REFCOUNT = 32,
+2 -2
View File
@@ -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
View File
@@ -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;
+6
View File
@@ -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
View File
@@ -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) {
+23
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large. Load diff
+16 -12
View File
@@ -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 */
+1
View File
@@ -71,6 +71,7 @@ struct lexer_param;
"<=" { return LESSEQ; }
">=" { return GREATEREQ; }
".." { return REC; }
"?//" { return ALTERNATION; }
"."|"?"|"="|";"|","|":"|"|"|"+"|"-"|"*"|"/"|"%"|"\$"|"<"|">" { return yytext[0];}
"["|"{"|"(" {
+142 -2
View File
@@ -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
+65 -27
View File
@@ -19,6 +19,7 @@
#include "parser.h"
#include "util.h"
#include "compile.h"
#include "jv_alloc.h"
struct lib_loading_state {
char **names;
@@ -97,12 +98,9 @@ static jv validate_relpath(jv name) {
return res;
}
jv components = jv_string_split(jv_copy(name), jv_string("/"));
jv rp = jv_array_get(jv_copy(components), 0);
components = jv_array_slice(components, 1, jv_array_length(jv_copy(components)));
jv_array_foreach(components, i, x) {
if (!strcmp(jv_string_value(x), "..")) {
jv_free(x);
jv_free(rp);
jv_free(components);
jv res = jv_invalid_with_msg(jv_string_fmt("Relative paths to modules may not traverse to parent directories (%s)", s));
jv_free(name);
@@ -110,18 +108,16 @@ static jv validate_relpath(jv name) {
}
if (i > 0 && jv_equal(jv_copy(x), jv_array_get(jv_copy(components), i - 1))) {
jv_free(x);
jv_free(rp);
jv_free(components);
jv res = jv_invalid_with_msg(jv_string_fmt("module names must not have equal consecutive components: %s",
jv_string_value(name)));
jv_free(name);
return res;
}
rp = jv_string_concat(rp, jv_string_concat(jv_string("/"), x));
jv_free(x);
}
jv_free(components);
jv_free(name);
return rp;
return name;
}
// Assumes name has been validated
@@ -137,10 +133,20 @@ static jv jv_basename(jv name) {
// Asummes validated relative path to module
static jv find_lib(jq_state *jq, jv rel_path, jv search, const char *suffix, jv jq_origin, jv lib_origin) {
if (jv_get_kind(search) != JV_KIND_ARRAY)
return jv_invalid_with_msg(jv_string_fmt("Module search path must be an array"));
if (jv_get_kind(rel_path) != JV_KIND_STRING)
if (!jv_is_valid(rel_path)) {
jv_free(search);
return rel_path;
}
if (jv_get_kind(rel_path) != JV_KIND_STRING) {
jv_free(rel_path);
jv_free(search);
return jv_invalid_with_msg(jv_string_fmt("Module path must be a string"));
}
if (jv_get_kind(search) != JV_KIND_ARRAY) {
jv_free(rel_path);
jv_free(search);
return jv_invalid_with_msg(jv_string_fmt("Module search path must be an array"));
}
struct stat st;
int ret;
@@ -232,14 +238,17 @@ static int process_dependencies(jq_state *jq, jv jq_origin, jv lib_origin, block
jv deps = block_take_imports(src_block);
block bk = *src_block;
int nerrors = 0;
const char *as_str = NULL;
jv_array_foreach(deps, i, dep) {
const char *as_str = NULL;
int is_data = jv_get_kind(jv_object_get(jv_copy(dep), jv_string("is_data"))) == JV_KIND_TRUE;
int raw = 0;
jv v = jv_object_get(jv_copy(dep), jv_string("raw"));
if (jv_get_kind(v) == JV_KIND_TRUE)
raw = 1;
int optional = 0;
if (jv_get_kind(jv_object_get(jv_copy(dep), jv_string("optional"))) == JV_KIND_TRUE)
optional = 1;
jv_free(v);
jv relpath = validate_relpath(jv_object_get(jv_copy(dep), jv_string("relpath")));
jv as = jv_object_get(jv_copy(dep), jv_string("as"));
@@ -253,33 +262,51 @@ static int process_dependencies(jq_state *jq, jv jq_origin, jv lib_origin, block
jv resolved = find_lib(jq, relpath, search, is_data ? ".json" : ".jq", jv_copy(jq_origin), jv_copy(lib_origin));
// XXX ...move the rest of this into a callback.
if (!jv_is_valid(resolved)) {
jv_free(as);
if (optional) {
jv_free(resolved);
continue;
}
jv emsg = jv_invalid_get_msg(resolved);
jq_report_error(jq, jv_string_fmt("jq: error: %s\n",jv_string_value(emsg)));
jv_free(emsg);
jv_free(as);
jv_free(deps);
jv_free(jq_origin);
jv_free(lib_origin);
return 1;
}
uint64_t state_idx = 0;
for (; state_idx < lib_state->ct; ++state_idx) {
if (strcmp(lib_state->names[state_idx],jv_string_value(resolved)) == 0)
break;
}
if (state_idx < lib_state->ct) { // Found
jv_free(resolved);
// Bind the library to the program
bk = block_bind_library(lib_state->defs[state_idx], bk, OP_IS_CALL_PSEUDO, as_str);
} else { // Not found. Add it to the table before binding.
block dep_def_block = gen_noop();
if (is_data) {
// Can't reuse data libs because the wrong name is bound
block dep_def_block;
nerrors += load_library(jq, resolved, is_data, raw, as_str, &dep_def_block, lib_state);
// resolved has been freed
if (nerrors == 0) {
// Bind the library to the program
// Bind as both $data::data and $data for backward compatibility vs common sense
bk = block_bind_library(dep_def_block, bk, OP_IS_CALL_PSEUDO, as_str);
bk = block_bind_library(dep_def_block, bk, OP_IS_CALL_PSEUDO, NULL);
}
} else {
uint64_t state_idx = 0;
for (; state_idx < lib_state->ct; ++state_idx) {
if (strcmp(lib_state->names[state_idx],jv_string_value(resolved)) == 0)
break;
}
if (state_idx < lib_state->ct) { // Found
jv_free(resolved);
// Bind the library to the program
bk = block_bind_library(lib_state->defs[state_idx], bk, OP_IS_CALL_PSEUDO, as_str);
} else { // Not found. Add it to the table before binding.
block dep_def_block = gen_noop();
nerrors += load_library(jq, resolved, is_data, raw, as_str, &dep_def_block, lib_state);
// resolved has been freed
if (nerrors == 0) {
// Bind the library to the program
bk = block_bind_library(dep_def_block, bk, OP_IS_CALL_PSEUDO, as_str);
}
}
}
jv_free(as);
}
jv_free(lib_origin);
@@ -321,11 +348,12 @@ static int load_library(jq_state *jq, jv lib_path, int is_data, int raw, const c
jv_string(dirname(lib_origin)),
&program, lib_state);
free(lib_origin);
program = block_bind_self(program, OP_IS_CALL_PSEUDO);
}
}
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;
@@ -372,6 +400,16 @@ int load_program(jq_state *jq, struct locfile* src, block *out_block) {
if (nerrors)
return nerrors;
char* home = getenv("HOME");
if (home) { // silently ignore no $HOME
/* Import ~/.jq as a library named "" found in $HOME */
block import = gen_import_meta(gen_import("", NULL, 0),
gen_const(JV_OBJECT(
jv_string("optional"), jv_true(),
jv_string("search"), jv_string(home))));
program = BLOCK(import, program);
}
nerrors = process_dependencies(jq, jq_get_jq_origin(jq), jq_get_prog_origin(jq), &program, &lib_state);
block libs = gen_noop();
for (uint64_t i = 0; i < lib_state.ct; ++i) {
+1 -1
View File
@@ -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++) {
+228 -110
View File
@@ -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,13 +138,25 @@ 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;
enum {
JQ_OK = 0,
JQ_OK_NULL_KIND = -1, /* exit 0 if --exit-status is not set*/
JQ_ERROR_SYSTEM = 2,
JQ_ERROR_COMPILE = 3,
JQ_OK_NO_OUTPUT = -4, /* exit 0 if --exit-status is not set*/
JQ_ERROR_UNKNOWN = 5,
};
#define jq_exit_with_status(r) exit(abs(r))
#define jq_exit(r) exit( r > 0 ? r : 0 )
static const char *skip_shebang(const char *p) {
if (strncmp(p, "#!", sizeof("#!") - 1) != 0)
return p;
@@ -145,19 +173,23 @@ static const char *skip_shebang(const char *p) {
}
static int process(jq_state *jq, jv value, int flags, int dumpopts) {
int ret = 14; // No valid results && -e -> exit(4)
int ret = JQ_OK_NO_OUTPUT; // No valid results && -e -> exit(4)
jq_start(jq, value, flags);
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);
ret = 0;
if (options & ASCII_OUTPUT) {
jv_dumpf(jv_copy(result), stdout, JV_PRINT_ASCII);
} else {
fwrite(jv_string_value(result), 1, jv_string_length_bytes(jv_copy(result)), stdout);
}
ret = JQ_OK;
jv_free(result);
} else {
if (jv_get_kind(result) == JV_KIND_FALSE || jv_get_kind(result) == JV_KIND_NULL)
ret = 11;
ret = JQ_OK_NULL_KIND;
else
ret = 0;
ret = JQ_OK;
if (options & SEQ)
priv_fwrite("\036", 1, stdout, dumpopts & JV_PRINT_ISATTY);
jv_dump(result, dumpopts);
@@ -167,7 +199,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 = JQ_OK;
else if (jv_get_kind(exit_code) == JV_KIND_NUMBER)
ret = jv_number_value(exit_code);
else
ret = JQ_ERROR_UNKNOWN;
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);
@@ -179,7 +233,7 @@ static int process(jq_state *jq, jv value, int flags, int dumpopts) {
fprintf(stderr, "jq: error (at %s) (not a string): %s\n",
jv_string_value(input_pos), jv_string_value(msg));
}
ret = 5;
ret = JQ_ERROR_UNKNOWN;
jv_free(input_pos);
jv_free(msg);
}
@@ -193,25 +247,12 @@ static void debug_cb(void *data, jv input) {
fprintf(stderr, "\n");
}
int main(int argc, char* argv[]) {
jq_state *jq = NULL;
int ret = 0;
int compiled = 0;
int parser_flags = 0;
int nfiles = 0;
int badwrite;
jv program_arguments = jv_array();
#ifdef WIN32
SetConsoleOutputCP(CP_UTF8);
fflush(stdout);
fflush(stderr);
_setmode(fileno(stdout), _O_TEXT | _O_U8TEXT);
_setmode(fileno(stderr), _O_TEXT | _O_U8TEXT);
int wargc;
wchar_t **wargv = CommandLineToArgvW(GetCommandLineW(), &wargc);
assert(wargc == argc);
int umain(int argc, char* argv[]);
int wmain(int argc, wchar_t* wargv[]) {
size_t arg_sz;
char **argv = alloca(argc * sizeof(wchar_t*));
for (int i = 0; i < argc; i++) {
argv[i] = alloca((arg_sz = WideCharToMultiByte(CP_UTF8,
0,
@@ -219,6 +260,28 @@ int main(int argc, char* argv[]) {
-1, 0, 0, 0, 0)));
WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, argv[i], arg_sz, 0, 0);
}
return umain(argc, argv);
}
int umain(int argc, char* argv[]) {
#else /*}*/
int main(int argc, char* argv[]) {
#endif
jq_state *jq = NULL;
int ret = JQ_OK_NO_OUTPUT;
int compiled = 0;
int parser_flags = 0;
int nfiles = 0;
int last_result = -1; /* -1 = no result, 0=null or false, 1=true */
int badwrite;
jv ARGS = jv_array(); /* positional arguments */
jv program_arguments = jv_object(); /* named arguments */
#ifdef WIN32
fflush(stdout);
fflush(stderr);
_setmode(fileno(stdout), _O_TEXT | _O_U8TEXT);
_setmode(fileno(stderr), _O_TEXT | _O_U8TEXT);
#endif
if (argc) progname = argv[0];
@@ -226,7 +289,7 @@ int main(int argc, char* argv[]) {
jq = jq_init();
if (jq == NULL) {
perror("malloc");
ret = 2;
ret = JQ_ERROR_SYSTEM;
goto out;
}
@@ -235,21 +298,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 +372,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 +397,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,100 +412,113 @@ 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 = JQ_ERROR_SYSTEM;
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)) {
printf("jq-%s\n", JQ_VERSION);
ret = 0;
ret = JQ_OK;
goto out;
}
if (isoption(argv[i], 0, "run-tests", &short_opts)) {
@@ -451,11 +541,20 @@ int main(int argc, char* argv[]) {
#ifdef USE_ISATTY
if (isatty(STDOUT_FILENO)) {
dumpopts |= JV_PRINT_ISATTY;
#ifndef WIN32
/* Disable color by default on Windows builds as Windows
terminals tend not to display it correctly */
dumpopts |= JV_PRINT_COLOR;
dumpopts |= JV_PRINT_ISATTY | JV_PRINT_COLOR;
#else
/* Verify we actually have the console, as the NUL device is also regarded as
tty. Windows can handle color if ANSICON (or ConEmu) is installed, or
Windows 10 supports the virtual terminal */
DWORD mode;
HANDLE con = GetStdHandle(STD_OUTPUT_HANDLE);
if (GetConsoleMode(con, &mode)) {
dumpopts |= JV_PRINT_ISATTY;
if (getenv("ANSICON") != NULL ||
SetConsoleMode(con, mode | 4/*ENABLE_VIRTUAL_TERMINAL_PROCESSING*/))
dumpopts |= JV_PRINT_COLOR;
}
#endif
}
#endif
@@ -464,6 +563,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 +592,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);
@@ -504,19 +606,25 @@ int main(int argc, char* argv[]) {
data = jv_invalid_get_msg(data);
fprintf(stderr, "%s: %s\n", progname, jv_string_value(data));
jv_free(data);
ret = 2;
ret = JQ_ERROR_SYSTEM;
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){
ret = 3;
ret = JQ_ERROR_COMPILE;
goto out;
}
@@ -550,6 +658,8 @@ int main(int argc, char* argv[]) {
(jv_is_valid((value = jq_util_input_next_input(input_state))) || jv_invalid_has_msg(jv_copy(value)))) {
if (jv_is_valid(value)) {
ret = process(jq, value, jq_flags, dumpopts);
if (ret <= 0 && ret != JQ_OK_NO_OUTPUT)
last_result = (ret != JQ_OK_NULL_KIND);
continue;
}
@@ -557,7 +667,7 @@ int main(int argc, char* argv[]) {
jv msg = jv_invalid_get_msg(value);
if (!(options & SEQ)) {
// --seq -> errors are not fatal
ret = 4;
ret = JQ_OK_NO_OUTPUT;
fprintf(stderr, "parse error: %s\n", jv_string_value(msg));
jv_free(msg);
break;
@@ -568,21 +678,29 @@ int main(int argc, char* argv[]) {
}
if (jq_util_input_errors(input_state) != 0)
ret = 2;
ret = JQ_ERROR_SYSTEM;
out:
badwrite = ferror(stdout);
if (fclose(stdout)!=0 || badwrite) {
fprintf(stderr,"Error: writing output failed: %s\n", strerror(errno));
ret = 2;
ret = JQ_ERROR_SYSTEM;
}
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)
return 0;
return ret;
if (options & (EXIT_STATUS|EXIT_STATUS_EXACT)) {
if (ret != JQ_OK_NO_OUTPUT)
jq_exit_with_status(ret);
else
switch (last_result) {
case -1: jq_exit_with_status(JQ_OK_NO_OUTPUT);
case 0: jq_exit_with_status(JQ_OK_NULL_KIND);
default: jq_exit_with_status(JQ_OK);
}
} else
jq_exit(ret);
}
+4
View File
@@ -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)
+1265 -1160
View File
File diff suppressed because it is too large. Load diff
+25 -21
View File
@@ -1,8 +1,8 @@
/* A Bison parser, made by GNU Bison 3.0.2. */
/* A Bison parser, made by GNU Bison 3.0.5. */
/* 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, 2018 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
@@ -40,7 +40,7 @@
extern int yydebug;
#endif
/* "%code requires" blocks. */
#line 11 "src/parser.y" /* yacc.c:1909 */
#line 11 "src/parser.y" /* yacc.c:1910 */
#include "locfile.h"
struct lexer_param;
@@ -57,7 +57,7 @@ struct lexer_param;
} \
} while (0)
#line 61 "src/parser.h" /* yacc.c:1909 */
#line 61 "src/parser.h" /* yacc.c:1910 */
/* Token type. */
#ifndef YYTOKENTYPE
@@ -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,26 +149,29 @@ 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 */
#line 31 "src/parser.y" /* yacc.c:1910 */
jv literal;
block blk;
#line 170 "src/parser.h" /* yacc.c:1909 */
#line 172 "src/parser.h" /* yacc.c:1910 */
};
typedef union YYSTYPE YYSTYPE;
# define YYSTYPE_IS_TRIVIAL 1
# define YYSTYPE_IS_DECLARED 1
#endif
+33 -11
View File
@@ -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));
}
@@ -334,7 +331,7 @@ FuncDefs:
$$ = gen_noop();
} |
FuncDef FuncDefs {
$$ = block_bind($1, $2, OP_IS_CALL_PSEUDO);
$$ = block_join($1, $2);
}
Exp:
@@ -342,25 +339,27 @@ 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());
} |
"if" Exp "then" Exp ElseBody {
$$ = gen_cond($2, $4, $5);
} |
"if" Exp "then" Exp "end" {
$$ = gen_cond($2, $4, gen_noop());
} |
"if" Exp "then" error {
FAIL(@$, "Possibly unterminated 'if' statement");
$$ = $2;
@@ -778,6 +777,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 +825,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);
} |
@@ -913,6 +931,10 @@ IDENT ':' ExpD {
$$ = gen_dictpair($1, BLOCK(gen_op_simple(POP), gen_op_simple(DUP2),
gen_op_simple(DUP2), gen_op_simple(INDEX)));
}
| '$' IDENT ':' ExpD {
$$ = gen_dictpair(gen_location(@$, locations, gen_op_unbound(LOADV, jv_string_value($2))),
$4);
}
| '$' IDENT {
$$ = gen_dictpair(gen_const($2),
gen_location(@$, locations, gen_op_unbound(LOADV, jv_string_value($2))));
+17 -10
View File
@@ -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);
@@ -309,7 +309,7 @@ static int jq_util_input_read_more(jq_util_input_state *state) {
state->buf_valid_len = 0;
if (state->current_input) {
char *res;
memset(state->buf, 0, sizeof(state->buf));
memset(state->buf, 0xff, sizeof(state->buf));
while (!(res = fgets(state->buf, sizeof(state->buf), state->current_input)) &&
ferror(state->current_input) && errno == EINTR)
@@ -334,20 +334,22 @@ static int jq_util_input_read_more(jq_util_input_state *state) {
size_t i;
/*
* XXX We can't know how many bytes we've read!
* XXX We don't know how many bytes we've read!
*
* We can't use getline() because there need not be any newlines
* in the input. The only entirely correct choices are: use
* fgetc() or fread(). Using fread() will complicate buffer
* management here.
*
* For now we guess how much fgets() read.
* For now we check how much fgets() read by scanning backwards for the
* terminating '\0'. This only works because we previously memset our
* buffer with something nonzero.
*/
for (p = state->buf, i = 0; i < sizeof(state->buf); i++) {
if (state->buf[i] != '\0')
p = &state->buf[i];
for (p = state->buf, i = sizeof(state->buf) - 1; i > 0; i--) {
if (state->buf[i] == '\0')
break;
}
state->buf_valid_len = p - state->buf + 1;
state->buf_valid_len = i;
} else if (p == NULL) {
state->buf_valid_len = sizeof(state->buf) - 1;
} else {
@@ -410,6 +412,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 +444,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 +457,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;
+35
View File
@@ -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"
+5
View File
@@ -0,0 +1,5 @@
#!/bin/sh
. "${0%/*}/setup" "$@"
$VALGRIND $Q $JQ -L "$mods" --run-tests $JQTESTDIR/base64.test
+265 -3
View File
@@ -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]
@@ -299,10 +304,23 @@ null
[11,22,33,44,55,66,77,88,99]
[11,22,33]
[limit(0; error)]
"badness"
[]
[limit(1; 1, error)]
"badness"
[1]
[first(range(.)), last(range(.)), nth(0; range(.)), nth(5; range(.)), try nth(-1; range(.)) catch .]
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 +391,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 +713,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 +976,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]]
@@ -883,6 +1044,19 @@ null
null
[5,6]
[if true then 3 end]
7
[3]
[if false then 3 end]
7
[7]
[if false then 3 else . end]
7
[7]
[.[] | [.foo[] // .bar]]
[{"foo":[1,2], "bar": 42}, {"foo":[1], "bar": null}, {"foo":[null,false,3], "bar": 18}, {"foo":[], "bar":42}, {"foo": [null,false,null], "bar": 41}]
[[1,2], [1], [3], [42], [41]]
@@ -938,6 +1112,28 @@ null
{}
[true, true, false]
# containment operator (embedded NULs!)
[contains(""), contains("\u0000")]
"\u0000"
[true, true]
[contains(""), contains("a"), contains("ab"), contains("c"), contains("d")]
"ab\u0000cd"
[true, true, true, true, true]
[contains("cd"), contains("b\u0000"), contains("ab\u0000")]
"ab\u0000cd"
[true, true, true]
[contains("b\u0000c"), contains("b\u0000cd"), contains("b\u0000cd")]
"ab\u0000cd"
[true, true, true]
[contains("@"), contains("\u0000@"), contains("\u0000what")]
"ab\u0000cd"
[false, false, false]
# Try/catch and general `?` operator
[.[]|try if . == 0 then error("foo") elif . == 1 then .a elif . == 2 then empty else . end catch .]
[0,1,2,3]
@@ -1044,7 +1240,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]
@@ -1211,6 +1413,10 @@ include "c"; [a, c]
null
[0,"acmehbah"]
import "data" as $e; import "data" as $d; [$d[].this,$e[].that,$d::d[].this,$e::e[].that]|join(";")
null
"is a test;is too;is a test;is too"
%%FAIL
module (.+1); 0
jq: error: Module metadata must be constant at <top-level>, line 1:
@@ -1306,4 +1512,60 @@ 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
# Regression test for #1815
index("")
""
null
+1 -1
View File
@@ -3,5 +3,5 @@
. "${0%/*}/setup" "$@"
# We set PAGER because there's a mantest for `env` that uses it.
(cd $JQBASEDIR/docs && rake mantests) |
(cd $JQBASEDIR/docs && pipenv run python3 build_mantests.py) |
env PAGER=less $VALGRIND $Q $JQ -L "$mods" --run-tests
+7 -11
View File
@@ -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
View File
@@ -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
+141 -29
View File
@@ -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
@@ -100,14 +96,20 @@ cmp $d/out $d/expected
cat > $d/expected <<EOF
ignoring parse error: Unfinished abandoned text at EOF at line 1, column 4
EOF
printf '"foo' | $JQ -ce --seq . > $d/out 2>&1
printf '"foo' | $JQ -c --seq . > $d/out 2>&1
cmp $d/out $d/expected
# with -e option should give 4 here as there's no valid output after
# ignoring parse errors with --seq.
printf '"foo' | $JQ -ce --seq . > $d/out 2>&1 || ret=$?
[ $ret -eq 4 ]
cmp $d/out $d/expected
# Numeric values truncated by EOF are ignored
cat > $d/expected <<EOF
ignoring parse error: Unfinished abandoned text at EOF at line 1, column 1
EOF
printf '1' | $JQ -ce --seq . > $d/out 2>&1
printf '1' | $JQ -c --seq . > $d/out 2>&1
cmp $d/out $d/expected
cat > $d/expected <<EOF
@@ -119,6 +121,21 @@ if printf '1\n' | $JQ -cen --seq '[inputs] == []' >/dev/null 2> $d/out; then
fi
cmp $d/out $d/expected
## Test --exit-status
data='{"i": 1}\n{"i": 2}\n{"i": 3}\n'
echo "$data" | $JQ --exit-status 'select(.i==1)' > /dev/null 2>&1
echo "$data" | $JQ --exit-status 'select(.i==2)' > /dev/null 2>&1
echo "$data" | $JQ --exit-status 'select(.i==3)' > /dev/null 2>&1
ret=0
echo "$data" | $JQ --exit-status 'select(.i==4)' > /dev/null 2>&1 || ret=$?
[ $ret -eq 4 ]
ret=0
echo "$data" | $JQ --exit-status 'select(.i==2) | false' > /dev/null 2>&1 || ret=$?
[ $ret -eq 1 ]
echo "$data" | $JQ --exit-status 'select(.i==2) | true' > /dev/null 2>&1
# Regression test for #951
printf '"a\n' > $d/input
if $VALGRIND $Q $JQ -e . $d/input; then
@@ -126,6 +143,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 +166,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 +199,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 +218,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 +240,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
+3
View File
@@ -0,0 +1,3 @@
def fill:"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
def e:"日本語";
e == "日本語"
Executable
+10
View File
@@ -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