Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f251344b7 | ||
|
|
21309bc6d7 | ||
|
|
8295baf284 | ||
|
|
ec84f28630 | ||
|
|
701a47c82e | ||
|
|
616f365e5a | ||
|
|
ceb2684f2f | ||
|
|
b9c340a211 | ||
|
|
3a964e2025 | ||
|
|
3d49fc59dd | ||
|
|
16e8d0b1ab | ||
|
|
5856a2d2c0 | ||
|
|
227f62e013 | ||
|
|
89244cc3ea | ||
|
|
3a43e2ff07 | ||
|
|
81f328f1aa | ||
|
|
f5a7bba986 | ||
|
|
66ef8e2c24 | ||
|
|
1628bbf95f | ||
|
|
812703139c | ||
|
|
54add5ac9a | ||
|
|
f183b57ed5 | ||
|
|
dc4dfdff17 | ||
|
|
f55034d314 | ||
|
|
930899e12d | ||
|
|
579518c78d | ||
|
|
fa45a5121c | ||
|
|
862566a48f | ||
|
|
f9f4d2d1e5 | ||
|
|
b74379532d | ||
|
|
1e5e0c9fe2 | ||
|
|
05899f8b3e | ||
|
|
a2a6f2d896 | ||
|
|
1c350c67d7 | ||
|
|
2348b7245e | ||
|
|
6e27de4f74 | ||
|
|
e5dafaea44 | ||
|
|
fd4f4f2087 | ||
|
|
6c89186437 | ||
|
|
32c4759e6b | ||
|
|
6366cfd7a4 | ||
|
|
9985c01975 | ||
|
|
e6371712ff | ||
|
|
d7e35101c5 | ||
|
|
1defaa7b00 | ||
|
|
815b4c9772 | ||
|
|
aaf305868c | ||
|
|
8cc31f0f49 | ||
|
|
a011ac6392 | ||
|
|
505e23124f | ||
|
|
e7f6d1e6d0 | ||
|
|
a220482de6 | ||
|
|
e16bd17571 | ||
|
|
25d47ca08e | ||
|
|
5108a451ca | ||
|
|
ec7647c4ab | ||
|
|
8aecf82bfe | ||
|
|
10cbb9b580 | ||
|
|
4a316fbb5a | ||
|
|
7811ef1e17 | ||
|
|
b04cd6aada | ||
|
|
1146b8b84a | ||
|
|
292f2d3208 | ||
|
|
3cbefde376 | ||
|
|
0b42457929 | ||
|
|
51a81c96f1 | ||
|
|
6083581fea | ||
|
|
164b877bfa | ||
|
|
b9c2a326ba | ||
|
|
bdc1feb50e | ||
|
|
4ef04e5b5c | ||
|
|
0f5345e515 | ||
|
|
c95b4a8ab4 | ||
|
|
520b429ca0 | ||
|
|
d3343d5113 | ||
|
|
2e96fb2c9b | ||
|
|
24a5e5b1b1 | ||
|
|
74ecb88935 | ||
|
|
33874eb513 | ||
|
|
1de56bc5df | ||
|
|
8a79f1d0a3 | ||
|
|
f4f5d8882d | ||
|
|
4451c08007 | ||
|
|
89c57f7936 | ||
|
|
6af7857ccd | ||
|
|
dff96aebcf | ||
|
|
8af52aaf52 | ||
|
|
d66e434fb4 | ||
|
|
3700b457aa | ||
|
|
11f084f9a7 | ||
|
|
454a9dc042 | ||
|
|
ff458803ce | ||
|
|
110e009996 | ||
|
|
7b6a018dff | ||
|
|
a50e548cc5 | ||
|
|
4b258f7d31 | ||
|
|
d1cb8ee0ad | ||
|
|
c7f063188f | ||
|
|
a6460f4fe2 | ||
|
|
2e257563a6 | ||
|
|
ad0090964b | ||
|
|
cbdaeb4062 | ||
|
|
cf1306f469 | ||
|
|
7675f7434a | ||
|
|
821d6404b6 | ||
|
|
e3cb1f76cd | ||
|
|
ef12efbf8c | ||
|
|
adb9b9b8a7 | ||
|
|
07f94f82a2 | ||
|
|
215b4c0fe1 | ||
|
|
89963e1370 | ||
|
|
5d40f3a7c0 | ||
|
|
5e0e7a7ebd | ||
|
|
dad6e42934 | ||
|
|
7d938487dd | ||
|
|
fcfacb932d | ||
|
|
6906d291c5 | ||
|
|
943090846f | ||
|
|
2dc8621d05 | ||
|
|
b6cc00fa71 | ||
|
|
9e2d9b6419 | ||
|
|
3210b29bba | ||
|
|
ddad9618dc | ||
|
|
24005287f4 | ||
|
|
1dcfc2f547 | ||
|
|
7d6d4066dd | ||
|
|
4c22bda09b | ||
|
|
9d62c6fd0c | ||
|
|
5e0db6dd65 | ||
|
|
551d875be1 | ||
|
|
4f8567476d | ||
|
|
ffa2832e33 | ||
|
|
0f7759f831 | ||
|
|
ccfba00178 | ||
|
|
3e8183fcd5 | ||
|
|
0df387f6b5 | ||
|
|
4104c4fa1c | ||
|
|
33a85679b9 | ||
|
|
a4b9552c82 | ||
|
|
b82c231900 | ||
|
|
3d2ab93b11 | ||
|
|
c86ef36769 | ||
|
|
3b8d08177f | ||
|
|
0164ff0bbc | ||
|
|
a4baac2256 | ||
|
|
1e5e9f3ef8 | ||
|
|
d58d85af9e | ||
|
|
10e8d34fd3 | ||
|
|
ebc090ad15 | ||
|
|
8cef5a37ab | ||
|
|
8afdeee785 | ||
|
|
a83a9e0bc2 | ||
|
|
0d414471bb | ||
|
|
91fb3df495 | ||
|
|
373ef71feb | ||
|
|
ca78a746e6 | ||
|
|
1e13e1e06e | ||
|
|
42ff8a6959 | ||
|
|
2e92c3e568 | ||
|
|
5389fdb651 | ||
|
|
7a295b30e0 | ||
|
|
cca3e92854 | ||
|
|
4d05dc55a3 | ||
|
|
aeb52e29d6 | ||
|
|
97602456e3 | ||
|
|
8b5ff40402 | ||
|
|
d630597e87 | ||
|
|
60d1ecb58a | ||
|
|
d927a2c185 | ||
|
|
6e7cf81d74 | ||
|
|
736f1751ee | ||
|
|
28aab9ca79 | ||
|
|
972c048149 | ||
|
|
902aa39fce | ||
|
|
22f55aa585 | ||
|
|
157c95b988 | ||
|
|
426913f127 | ||
|
|
c4cc62a053 | ||
|
|
862696a778 | ||
|
|
86bc662bd2 | ||
|
|
ae7f8d6ab9 | ||
|
|
7dc34b92af | ||
|
|
cbfc0d6130 | ||
|
|
c308b2881f | ||
|
|
c959d1fca1 | ||
|
|
63511a874f | ||
|
|
90c705f138 | ||
|
|
b67bad82cb | ||
|
|
6981b46338 | ||
|
|
752e00bc66 | ||
|
|
00f018a054 | ||
|
|
56344670f4 | ||
|
|
5df20f4954 | ||
|
|
a9c613e87d | ||
|
|
7538ef34be | ||
|
|
5c62a4b805 | ||
|
|
559863ca68 | ||
|
|
845ad5e969 | ||
|
|
243b1f8e5e | ||
|
|
5bfb9781f7 | ||
|
|
906d2537b9 | ||
|
|
bbb287f54b | ||
|
|
e51e916e31 | ||
|
|
46e3125e26 | ||
|
|
c02fcc8fef | ||
|
|
25d4cbd1b3 | ||
|
|
3234a7681e | ||
|
|
36077da66a | ||
|
|
9e1fd8cf4d | ||
|
|
a55085b662 | ||
|
|
e0f9b6a5cd | ||
|
|
0053aa868c | ||
|
|
fab2048648 | ||
|
|
56ae88d9d5 | ||
|
|
1cdcff1c01 | ||
|
|
8ca07a08c3 | ||
|
|
2775b90b20 | ||
|
|
ae312bd7fe | ||
|
|
9017b83d4e | ||
|
|
02cf1831e9 | ||
|
|
d237de7b30 | ||
|
|
84d3203dd1 | ||
|
|
a3234034b5 | ||
|
|
b748eae035 | ||
|
|
a6656edc7c | ||
|
|
b4a9ea56ae | ||
|
|
fdbc91e3c4 | ||
|
|
bd67bb8273 | ||
|
|
54a0470c42 | ||
|
|
3dd790c408 | ||
|
|
be11b2768f | ||
|
|
2075bec60f | ||
|
|
44c2382402 | ||
|
|
fd041c2064 | ||
|
|
c6495f6632 | ||
|
|
7243989c52 | ||
|
|
46613af64c | ||
|
|
79000644c6 | ||
|
|
b349e7d9a8 | ||
|
|
6c8882caf3 | ||
|
|
30e00820a7 | ||
|
|
0f81e1bcd0 | ||
|
|
cb376b27f8 | ||
|
|
dc5e77ef8e | ||
|
|
72735167f9 | ||
|
|
89791a000b | ||
|
|
3411167c03 | ||
|
|
85f0e30c81 | ||
|
|
1796a716ea | ||
|
|
07d8d5c5ef | ||
|
|
0b29b44cab | ||
|
|
a0a350f468 | ||
|
|
4a57b84db0 | ||
|
|
f7a2af7052 | ||
|
|
456bafa82f | ||
|
|
7a8e3c759e | ||
|
|
b70bea8d34 | ||
|
|
d177944b75 | ||
|
|
9bd55487ea | ||
|
|
23e2e2eab3 | ||
|
|
8d2d5e37e5 | ||
|
|
287e2c96ed | ||
|
|
f8d9478517 | ||
|
|
1ba8c2cfa6 | ||
|
|
d654724fef | ||
|
|
3375089013 | ||
|
|
9f13afa20f | ||
|
|
908c10928b | ||
|
|
c321c3b86b | ||
|
|
f87521183f | ||
|
|
8a561120c4 | ||
|
|
20ca21cb0d | ||
|
|
2d1a75f550 | ||
|
|
8cddb7c681 | ||
|
|
4249bbf0d5 | ||
|
|
8a57c27e92 | ||
|
|
8706b6b16a | ||
|
|
32c08d48f3 | ||
|
|
fc52e78ed4 | ||
|
|
eb12f0358e | ||
|
|
a696c6b551 | ||
|
|
0d437e25de | ||
|
|
6744664ff4 | ||
|
|
ef5bd5a6de | ||
|
|
2159f9f5e0 | ||
|
|
2518b3a34f | ||
|
|
c3d6d5fd37 | ||
|
|
a25950a58c | ||
|
|
f8fed0f0bc | ||
|
|
89545ea9d0 | ||
|
|
a2f99d43c3 | ||
|
|
a7878a8d1a | ||
|
|
c73ad74be0 | ||
|
|
53993a9942 | ||
|
|
e1b20b48f0 | ||
|
|
14bc1f6526 | ||
|
|
2e2538ccb8 | ||
|
|
ae27178352 | ||
|
|
0f89270d08 | ||
|
|
10f5a3448d | ||
|
|
50b85c2911 | ||
|
|
38b939688a | ||
|
|
01fc8168e9 | ||
|
|
3362fb3406 | ||
|
|
8c3d503d54 | ||
|
|
2bb9fc5dda | ||
|
|
ea3e947b50 | ||
|
|
538e012e85 | ||
|
|
918ada06dc | ||
|
|
cb2737a433 | ||
|
|
c38e5b1b2c | ||
|
|
089041b090 | ||
|
|
cadf8c3cb0 | ||
|
|
6b6e3f4262 | ||
|
|
6e88d419ba | ||
|
|
ab4bf05300 | ||
|
|
ecac7bd3a1 | ||
|
|
1d620c0de3 | ||
|
|
f68815d5ad | ||
|
|
666aeda82c | ||
|
|
5be80b81eb | ||
|
|
10477fc2d4 | ||
|
|
d8672e1b2a | ||
|
|
a68958e5dc | ||
|
|
824f7df404 | ||
|
|
4bcff40828 | ||
|
|
80a9937e48 | ||
|
|
b963ebaf1d | ||
|
|
eb45372414 | ||
|
|
539dccae90 | ||
|
|
6458910ba3 | ||
|
|
ef9b591215 | ||
|
|
f845808813 | ||
|
|
25719b5596 | ||
|
|
8780bc0b8e | ||
|
|
821cd31e67 | ||
|
|
5a863bf010 | ||
|
|
d0ca11d665 | ||
|
|
7d3a44a1e2 | ||
|
|
7fce34292e | ||
|
|
9113282c27 | ||
|
|
78a8419428 | ||
|
|
ff9a61ead2 | ||
|
|
3a647d3e47 | ||
|
|
1f17ef3623 | ||
|
|
a6030ab374 | ||
|
|
9f4d7e2fb8 | ||
|
|
ba1acbe961 | ||
|
|
9deee38dc8 | ||
|
|
436941d48b | ||
|
|
e1c5a2f575 | ||
|
|
a5e9086676 | ||
|
|
1204e328df | ||
|
|
3e1baf5916 | ||
|
|
beb0d081bb | ||
|
|
4b7c701fdd | ||
|
|
233e32208c | ||
|
|
63e31c2a35 | ||
|
|
f9349becab | ||
|
|
ff5cf0abc7 | ||
|
|
8ff935c01a | ||
|
|
5d9d1b1020 | ||
|
|
8725d9fa3e | ||
|
|
9ae2d66d21 | ||
|
|
0c762925b2 | ||
|
|
ad52026550 | ||
|
|
cf7895876b | ||
|
|
f902d5fae6 | ||
|
|
98e3ebc378 | ||
|
|
dde43f796e | ||
|
|
7ee3b724d1 | ||
|
|
e151a300d0 | ||
|
|
f58f771660 | ||
|
|
ea63d5d3c2 | ||
|
|
ea0ffe24f8 | ||
|
|
061a604a2a | ||
|
|
f741074267 | ||
|
|
de5952d67e | ||
|
|
a03f4802ef | ||
|
|
778ba9f7ab | ||
|
|
676e0ce38e | ||
|
|
088a42fc53 | ||
|
|
63dbac28ad | ||
|
|
e9a1de47b4 | ||
|
|
d14fca0cc6 | ||
|
|
bb83813f51 | ||
|
|
8225d4e0c5 | ||
|
|
d45195ab5c | ||
|
|
73bd347fcf | ||
|
|
c9dbe090c5 | ||
|
|
61cc0d4fb1 | ||
|
|
15c4a7f95f | ||
|
|
20e629284f | ||
|
|
e73951f3d1 | ||
|
|
fdf843c08b | ||
|
|
cf145ec65e | ||
|
|
4889844d0c | ||
|
|
dc7857c585 | ||
|
|
d65dbf89c9 | ||
|
|
ed2b3e26fb | ||
|
|
46436bd870 | ||
|
|
1dbe9317bc | ||
|
|
ef4f3a54fe | ||
|
|
9af03c2562 | ||
|
|
c10e5f4e73 | ||
|
|
01f676041d | ||
|
|
6856ab8a69 | ||
|
|
8212750fe9 | ||
|
|
35b6b949e3 | ||
|
|
d1ea3ab89d | ||
|
|
ae625d0de7 | ||
|
|
5fb82d1018 | ||
|
|
f9d1b94496 | ||
|
|
854ff5643a | ||
|
|
072ebb911b | ||
|
|
d46f5d29fa | ||
|
|
69aa0003af | ||
|
|
41a093c1f2 | ||
|
|
022a418894 | ||
|
|
d056668f4f | ||
|
|
917334d18a | ||
|
|
7a83f1c416 | ||
|
|
6f0a9521e1 | ||
|
|
aee5c3ecb0 | ||
|
|
da0d575649 | ||
|
|
74d2216da2 | ||
|
|
769314a8a2 | ||
|
|
831be04027 | ||
|
|
1a1adf73e8 | ||
|
|
68f41097b9 | ||
|
|
4402c6d806 | ||
|
|
2aa8a43c5b | ||
|
|
219bb9ecb7 | ||
|
|
88fd2ee4f2 | ||
|
|
6c3683dfe9 | ||
|
|
36e495da1e | ||
|
|
15faf9dc3b | ||
|
|
59302fd208 | ||
|
|
24202d1ecf | ||
|
|
336f4e27e6 | ||
|
|
fe29d3d3fa | ||
|
|
3d33412e9a | ||
|
|
fb9196021e | ||
|
|
b23fae9ee3 | ||
|
|
8b41415b07 | ||
|
|
1fa55a3fae | ||
|
|
a45c937f80 | ||
|
|
6ed3adb091 | ||
|
|
106fdf5114 | ||
|
|
99f170adc3 | ||
|
|
f60d83c15f | ||
|
|
36a47bae56 | ||
|
|
dc8528af0f | ||
|
|
e12d0ffd1e | ||
|
|
a71138a43a | ||
|
|
cb976b9a50 | ||
|
|
060eb39bf9 | ||
|
|
ab363e9e50 | ||
|
|
41a8ae7557 | ||
|
|
1956abbedf | ||
|
|
2003a0440f | ||
|
|
a0274cc46e | ||
|
|
3b384740b9 | ||
|
|
4d40a47eab | ||
|
|
18e828f17c | ||
|
|
0966230ba8 | ||
|
|
669f4ab4c3 | ||
|
|
3455714410 | ||
|
|
32ea50d336 | ||
|
|
5ac6a460fa | ||
|
|
f147b7fae5 | ||
|
|
474386a1d9 | ||
|
|
7fd2c2d08b | ||
|
|
5412f3f1c1 | ||
|
|
014b45b4a9 | ||
|
|
30ea390b77 | ||
|
|
00f244385b | ||
|
|
7b671b95d2 | ||
|
|
1535f23455 | ||
|
|
4ba0c8baef | ||
|
|
a7dd3ab793 | ||
|
|
f6f7d4d973 | ||
|
|
f4ae5c883b | ||
|
|
74a14f5de7 | ||
|
|
fee21c959a | ||
|
|
4b48054f41 | ||
|
|
17a319d120 | ||
|
|
3a1dab5396 | ||
|
|
dac081e183 | ||
|
|
489fcdc089 | ||
|
|
3ba7c97b2a | ||
|
|
54635000c5 | ||
|
|
426edff09c | ||
|
|
b37ce20c2a | ||
|
|
cac14a531d | ||
|
|
ae7a042876 | ||
|
|
eb165459aa | ||
|
|
04bc2ef7cf | ||
|
|
09104ce531 | ||
|
|
9fa2173825 | ||
|
|
346da34432 | ||
|
|
fa316ac430 | ||
|
|
77936a594d | ||
|
|
4e07eac356 | ||
|
|
d018e43ddc | ||
|
|
8cee66e609 | ||
|
|
3c4248192d | ||
|
|
255f8ef5be | ||
|
|
20b32db5af | ||
|
|
b634722c8a | ||
|
|
c9e8581f45 | ||
|
|
da129f6664 | ||
|
|
884e6c7d8b | ||
|
|
5aadaa79eb | ||
|
|
cf562961b7 | ||
|
|
63bed9bdf1 | ||
|
|
5989dbdfcf | ||
|
|
db19a11399 | ||
|
|
6c734cb609 | ||
|
|
ef41d1cb57 | ||
|
|
08b4a34a8e | ||
|
|
1fc5656762 | ||
|
|
91cfc34ae4 | ||
|
|
a0b942a7f5 | ||
|
|
6a401c8262 | ||
|
|
d235f32bc9 | ||
|
|
bdbfa7e32c | ||
|
|
78976b3504 | ||
|
|
8d4748c8df | ||
|
|
da723d3e11 | ||
|
|
0feeb7d0d4 | ||
|
|
9e53f36a74 | ||
|
|
7e7c08da47 | ||
|
|
2f6b89ef70 | ||
|
|
4e3024cb61 | ||
|
|
c427bf6a94 | ||
|
|
dae2422fd1 | ||
|
|
f98e3e6c26 | ||
|
|
fe99532f8b | ||
|
|
41a4968c90 | ||
|
|
1cdaabf2dd | ||
|
|
82d8253c19 | ||
|
|
c1717d6e12 | ||
|
|
f0d1c39b5b | ||
|
|
111aea3f0a | ||
|
|
37cfc912c1 | ||
|
|
8c2e228c74 | ||
|
|
8630fb02bb | ||
|
|
5052ab4e2b | ||
|
|
a44f739356 | ||
|
|
f44ecf3872 | ||
|
|
1c9e03f800 | ||
|
|
5d9ec83805 | ||
|
|
3a295c659b | ||
|
|
d253414783 | ||
|
|
8b1b503609 | ||
|
|
ff48bd6ec5 | ||
|
|
54b9c9bdb2 | ||
|
|
3dcdc582ea | ||
|
|
7c4171d414 | ||
|
|
1d839ff9df | ||
|
|
3403d07912 | ||
|
|
e8643787cf | ||
|
|
298b2a6033 | ||
|
|
c791428905 | ||
|
|
dc2fe63e42 | ||
|
|
7af88962ee | ||
|
|
3216d7683a | ||
|
|
e4094c8664 | ||
|
|
b49d65a276 | ||
|
|
824ce76cec | ||
|
|
2a18f97d12 | ||
|
|
8c4d29ee38 | ||
|
|
2c13f4fe72 | ||
|
|
94931040a8 | ||
|
|
b78a4cdc93 | ||
|
|
da09544bb0 | ||
|
|
ce17b6bd53 | ||
|
|
fe03bea191 | ||
|
|
0ed6347d47 | ||
|
|
3f86e97f70 | ||
|
|
69fa279e3c | ||
|
|
bbf4e05fde | ||
|
|
05d90517b0 | ||
|
|
5f5c1dc5a6 | ||
|
|
e0524644f8 | ||
|
|
81e2336ace | ||
|
|
25bf1a0169 | ||
|
|
c8ab67be27 | ||
|
|
6c34288ebf | ||
|
|
2ffbb9d6b7 | ||
|
|
dad608d5c0 | ||
|
|
04b939625b | ||
|
|
5dac485458 | ||
|
|
2c4ccd122e | ||
|
|
b90ae078b8 | ||
|
|
cc8761dbde | ||
|
|
08cc591884 | ||
|
|
1bc4d04f43 | ||
|
|
d4018192a9 | ||
|
|
19ddbfeca9 | ||
|
|
4fed6763fc | ||
|
|
73446a9cce | ||
|
|
dcf1ac0d1f | ||
|
|
364c58d13f | ||
|
|
8cfe11e4e2 | ||
|
|
40378a18f7 | ||
|
|
7fb5d8c973 | ||
|
|
6e373942e5 | ||
|
|
f0b982795c | ||
|
|
cbd422d56e | ||
|
|
03598bb5d6 | ||
|
|
983a53a643 |
No files matched your search
+26
-7
@@ -1,28 +1,47 @@
|
||||
*.o
|
||||
*.a
|
||||
*.lo
|
||||
*.la
|
||||
*.lai
|
||||
*.so
|
||||
*.so.*
|
||||
*~
|
||||
.*.sw[a-p]
|
||||
tags
|
||||
|
||||
jq
|
||||
!tests/modules/lib/jq/
|
||||
jq.1
|
||||
|
||||
# Something delightfully recursive happens otherwise
|
||||
docs/content/2.download/source/*
|
||||
|
||||
# Autogenerated
|
||||
lexer.c
|
||||
lexer.h
|
||||
parser.c
|
||||
parser.h
|
||||
|
||||
# Autotools junk
|
||||
.libs
|
||||
.deps
|
||||
libtool
|
||||
*.log
|
||||
stamp-h1
|
||||
config.log
|
||||
config.status
|
||||
autom4te.cache
|
||||
config.h
|
||||
INSTALL
|
||||
Makefile
|
||||
jq-*.tar.gz
|
||||
configure
|
||||
aclocal.m4
|
||||
Makefile.in
|
||||
version.h
|
||||
.remake-version-h
|
||||
config.cache
|
||||
*.rpm
|
||||
m4/libtool.m4
|
||||
m4/ltoptions.m4
|
||||
m4/ltsugar.m4
|
||||
m4/ltversion.m4
|
||||
m4/lt~obsolete.m4
|
||||
tests/*.trs
|
||||
|
||||
cscope.in.out
|
||||
cscope.out
|
||||
cscope.po.out
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
sudo: false
|
||||
|
||||
language: c
|
||||
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- compiler: gcc
|
||||
env: COVERAGE="--disable-valgrind --enable-gcov"
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libonig-dev
|
||||
- valgrind
|
||||
|
||||
before_install:
|
||||
- rm lexer.h lexer.c parser.h parser.c
|
||||
|
||||
install:
|
||||
- bundle install --gemfile=docs/Gemfile
|
||||
|
||||
- wget http://ftp.debian.org/debian/pool/main/b/bison/bison_3.0.2.dfsg-2_amd64.deb
|
||||
- ar p bison_3.0.2.dfsg-2_amd64.deb data.tar.xz | tar xJ
|
||||
|
||||
- if [ -n "$COVERAGE" ]; then pip install --user cpp-coveralls; fi
|
||||
|
||||
before_script:
|
||||
- autoreconf -i
|
||||
- ./configure
|
||||
YACC="usr/bin/bison -y"
|
||||
$COVERAGE
|
||||
|
||||
script:
|
||||
- make -j4 BISON_PKGDATADIR=$(pwd)/usr/share/bison
|
||||
- make check -j4
|
||||
|
||||
after_script:
|
||||
- |
|
||||
if [ -n "$COVERAGE" ]; then
|
||||
rm -rf .libs usr # don't care about coverage for libjq and bison
|
||||
coveralls -e lexer.c -e parser.c -e jv_dtoa.c --gcov-options '\-lp'
|
||||
fi
|
||||
|
||||
after_failure:
|
||||
- cat test-suite.log
|
||||
- cat tests/*.log
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
@@ -1,6 +1,73 @@
|
||||
Created By:
|
||||
Stephen Dolan <mu@netsoc.tcd.ie>
|
||||
|
||||
Contributions by
|
||||
Anthony Shortland <anthony.shortland@me.com> - rpmbuild target
|
||||
Lee Thompson <stagr.lee@gmail.com> - autoconf stuff
|
||||
Maintained by:
|
||||
Nicolas Williams <nico@cryptonector.com>
|
||||
William Langford <wlangfor@gmail.com>
|
||||
David Tolnay <dtolnay@gmail.com>
|
||||
|
||||
Contributions by:
|
||||
Andrew Rodland <andrew@cleverdomain.org> - bug fixes
|
||||
Ankur <ankz.kothari@gmail.com> - bug fixes
|
||||
Anthony Shortland <anthony.shortland@me.com> - rpmbuild target
|
||||
Brendan Macmillan <melbourne.research@gmail.com> - bug fixes, load library from ~/.jq
|
||||
David R. MacIver <david@drmaciver.com> - bug fixes
|
||||
David Tolnay <dtolnay@gmail.com> - destructuring, build improvements
|
||||
Filippo Giunchedi <fgiunchedi@gmail.com> - bug fixes
|
||||
Filippo Valsorda <filippo.valsorda@gmail.com> - recursive object merge (`*`)
|
||||
Jack Pearkes <jackpearkes@gmail.com> - update tutorial
|
||||
Jingwen Owen Ou <jingweno@gmail.com> - jqplay.org and link to it
|
||||
Joe Littlejohn <joe.littlejohn@nokia.com> - bug fixes
|
||||
Juan Guerrero <juan.guerrero.lozano@gmail.com> - bug fixes
|
||||
Kenny Shen <kenny.shen@zalora.com> - doc fixes
|
||||
Lee Thompson <stagr.lee@gmail.com> - autoconf stuff
|
||||
Markus Lanthaler <mark_lanthaler@gmx.net> - doc fixes
|
||||
Michael Daines <michael@mdaines.com> - add sqrt; doc fixes
|
||||
Mike McCabe <mccabe@archive.org> - math (libm) functions
|
||||
Nicolas Pouillard <nicolas.pouillard@gmail.com> - add tests
|
||||
Nicolas Williams <nico@cryptonector.com> - library-fication, autoconf stuff, exception handling, various
|
||||
Peter van Dijk <peter@7bits.nl> - doc fixes
|
||||
Philipp Hagemeister <phihag@phihag.de> - doc fixes
|
||||
polyester <paul@cleanclothes.org> - automake version update
|
||||
Rémy Léone <remy.leone@gmail.com> - add .travis.yml
|
||||
Ryoichi KATO <ryo1kato@gmail.com> - doc fixes
|
||||
Santiago Lapresta <santiago.lapresta@gmail.com> - join, arrays, all, any, other filters
|
||||
pkoppstein <pkoppstein@gmail.com> - various builtins, improvements
|
||||
13ren <melbourne.research@gmail.com>
|
||||
Aaron Peschel <apeschel@zendesk.com>
|
||||
Adam Lindberg <hello@alind.io>
|
||||
Alex Chamberlain <alex@alexchamberlain.co.uk>
|
||||
Andrew O'Brien <obrien.andrew@gmail.com> - docs build
|
||||
Assaf Gordon <assafgordon@gmail.com> - error handling
|
||||
cdnbacon <mike@busbud.com>
|
||||
Charles Merriam <charles.merriam@gmail.com>
|
||||
Colin von Heuring <colin@janrain.com>
|
||||
Damian Gryski <damian@gryski.com>
|
||||
David Haguenauer <ml@kurokatta.org>
|
||||
Doug Luce <doug@github.con.com> - build
|
||||
Eiichi Sato <sato.eiichi@gmail.com>
|
||||
Hanfei Shen <qqshfox@gmail.com>
|
||||
Ian Miell <ian.miell@gmail.com>
|
||||
i <isomorphisms@sdf.org>
|
||||
James Andariese <james.andariese@locationlabs.com> - Dockerfile
|
||||
jkleint <jkleint@gmail.com>
|
||||
Joel Purra <code+github@joelpurra.com>
|
||||
Kim De Mey <kim.demey@gmail.com> - build
|
||||
Kim Toms <kim.toms@bplglobal.net>
|
||||
LCD 47 <lcd047@gmail.com>
|
||||
Lee Thompson <thompson@dtosolutions.com> - rpm
|
||||
Marc Abramowitz <marc@marc-abramowitz.com>
|
||||
Marc Bruggmann <marcbr@spotify.com>
|
||||
Maxime Biais <maxime.biais@gmail.com> - build
|
||||
Mike Fletcher <john.m.fletcher@bankofamerica.com>
|
||||
Sebastian Freundt <freundt@ga-group.nl> - build
|
||||
Shay Elkin <shay@everything.me>
|
||||
Simon Elsbrock <simon@iodev.org> - Debian
|
||||
Stefan Seemayer <stefan@seemayer.de>
|
||||
Stephen Roantree <stroantree@gmail.com>
|
||||
Stephen Shaw <stesh@netsoc.tcd.ie>
|
||||
Steven Maude <StevenMaude@users.noreply.github.com>
|
||||
Steven Penny <svnpenn@gmail.com> - Windows bug fixes
|
||||
tal@whatexit.org <tal@whatexit.org>
|
||||
Travis Gockel <travis@gockelhut.com>
|
||||
Zhiming Wang <zmwangx@gmail.com>
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
FROM debian:8
|
||||
|
||||
COPY . /app
|
||||
|
||||
# get dependencies, build, and remove anything we don't need for running jq.
|
||||
# valgrind seems to have trouble with pthreads TLS so it's off.
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y \
|
||||
build-essential \
|
||||
autoconf \
|
||||
libtool \
|
||||
git \
|
||||
bison \
|
||||
flex \
|
||||
ruby \
|
||||
wget \
|
||||
ruby-dev && \
|
||||
wget http://www.geocities.jp/kosako3/oniguruma/archive/onig-5.9.6.tar.gz && \
|
||||
sha512sum onig-5.9.6.tar.gz | grep 4a181ea6f0e1a018bbaf6c87e666dfffd1ef4b8f5dcead07fa0b6564a76174e7e01854173924668433ae74c455dbced6a0e1b43e9066f0499b4a57e855e1a2b2 && \
|
||||
tar zxvf onig-5.9.6.tar.gz && \
|
||||
(cd onig-5.9.6 && \
|
||||
./configure --prefix=/usr/local && \
|
||||
make && \
|
||||
make install ) && \
|
||||
gem install bundler && \
|
||||
(cd /app/docs && bundle install) && \
|
||||
(cd /app && \
|
||||
autoreconf -i && \
|
||||
./configure --disable-valgrind --enable-all-static --prefix=/usr/local && \
|
||||
make -j8 && \
|
||||
make check && \
|
||||
make install && \
|
||||
make distclean ) && \
|
||||
(cd onig-5.9.6 && \
|
||||
make uninstall ) && \
|
||||
apt-get purge -y \
|
||||
build-essential \
|
||||
autoconf \
|
||||
libtool \
|
||||
bison \
|
||||
git \
|
||||
flex \
|
||||
ruby \
|
||||
ruby-dev && \
|
||||
apt-get autoremove -y && \
|
||||
rm -rf onig-5.9.6 && \
|
||||
rm -rf /var/lib/apt/lists/* /var/lib/gems
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/jq"]
|
||||
+103
-30
@@ -1,48 +1,100 @@
|
||||
|
||||
### C source files to be built and distributed.
|
||||
|
||||
JQ_INCS = jq_parser.h builtin.h bytecode.h compile.h execute.h \
|
||||
forkable_stack.h frame_layout.h jv.h jv_alloc.h jv_aux.h jv_dtoa.h \
|
||||
jv_parse.h jv_unicode.h locfile.h opcode.h opcode_list.h parser.y \
|
||||
jv_utf8_tables.h lexer.l
|
||||
|
||||
JQ_SRC = opcode.c bytecode.c compile.c execute.c builtin.c jv.c \
|
||||
jv_parse.c jv_print.c jv_dtoa.c jv_unicode.c jv_aux.c jv_alloc.c \
|
||||
jq_test.c ${JQ_INCS}
|
||||
LIBJQ_INCS = jq_parser.h builtin.h bytecode.h compile.h exec_stack.h \
|
||||
libm.h jv_alloc.h jv_dtoa.h jv_unicode.h locfile.h \
|
||||
opcode_list.h parser.y jv_utf8_tables.h lexer.l util.h linker.h
|
||||
|
||||
LIBJQ_SRC = locfile.c bytecode.c compile.c execute.c builtin.c jv.c \
|
||||
jv_parse.c jv_print.c jv_dtoa.c jv_unicode.c jv_aux.c jv_file.c \
|
||||
jv_alloc.c jq_test.c util.c linker.c ${LIBJQ_INCS}
|
||||
|
||||
### C build options
|
||||
|
||||
AM_CFLAGS = -Wextra -Wall -Wno-missing-field-initializers \
|
||||
-Wno-unused-parameter -Wno-unused-function
|
||||
|
||||
ACLOCAL_AMFLAGS = -I config/m4
|
||||
|
||||
### Generating the lexer and parser
|
||||
|
||||
# While there is some autoconf macro support for lex/flex, it doesn't support
|
||||
# header file creation so we'll use good old make
|
||||
BUILT_SOURCES = lexer.h lexer.c parser.h parser.c
|
||||
if MAINTAINER_MODE
|
||||
BUILT_SOURCES = lexer.h lexer.c parser.h parser.c version.h
|
||||
lexer.c: lexer.l
|
||||
flex -o lexer.c --header-file=lexer.h $<
|
||||
$(AM_V_LEX) flex -o lexer.c --header-file=lexer.h $<
|
||||
lexer.h: lexer.c
|
||||
else
|
||||
BUILT_SOURCES =
|
||||
.y.c:
|
||||
$(AM_V_YACC) echo "NOT building parser.c!"
|
||||
.l.c:
|
||||
$(AM_V_LEX) echo "NOT building lexer.c!"
|
||||
endif
|
||||
|
||||
# Tell YACC (bison) autoconf macros that you want a header file created.
|
||||
# If the --warnings=all fails, you probably have an old version of bison
|
||||
# OSX ships an old bison, so update with homebrew or macports
|
||||
AM_YFLAGS = --warnings=all -d
|
||||
|
||||
### libjq
|
||||
|
||||
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
|
||||
|
||||
include_HEADERS = jv.h jq.h
|
||||
|
||||
### Running tests under Valgrind
|
||||
|
||||
if ENABLE_VALGRIND
|
||||
NO_VALGRIND =
|
||||
else
|
||||
NO_VALGRIND = 1
|
||||
endif
|
||||
|
||||
### Code coverage with gcov
|
||||
|
||||
if ENABLE_GCOV
|
||||
AM_CFLAGS += --coverage --no-inline
|
||||
endif
|
||||
|
||||
### Error injection for testing
|
||||
|
||||
if ENABLE_ERROR_INJECTION
|
||||
lib_LTLIBRARIES += libinject_errors.la
|
||||
libinject_errors_la_SOURCES = inject_errors.c
|
||||
libinject_errors_la_LIBADD = -ldl
|
||||
libinject_errors_la_LDFLAGS = -module
|
||||
endif
|
||||
|
||||
### Building the jq binary
|
||||
|
||||
main.c: config.h
|
||||
# Remake the version.h header file if, and only if, the git ID has changed
|
||||
.PHONY: .FORCE
|
||||
.FORCE:
|
||||
generate_ver = ver="`{ $(srcdir)/scripts/version || echo '$(VERSION)' ; } | sed 's/.*/\#define JQ_VERSION \"&\"/'`"
|
||||
.remake-version-h: .FORCE
|
||||
@ $(generate_ver); test "x`cat version.h 2>/dev/null`" = "x$$ver" || touch .remake-version-h
|
||||
version.h: .remake-version-h
|
||||
$(AM_V_GEN) $(generate_ver); echo "$$ver" > $@
|
||||
main.c: version.h
|
||||
|
||||
bin_PROGRAMS = jq
|
||||
jq_SOURCES = ${JQ_SRC} main.c
|
||||
jq_SOURCES = main.c version.h
|
||||
jq_LDFLAGS = -static-libtool-libs
|
||||
jq_LDADD = libjq.la -lm
|
||||
|
||||
if ENABLE_ALL_STATIC
|
||||
jq_LDFLAGS += -all-static
|
||||
endif
|
||||
|
||||
### Tests (make check)
|
||||
|
||||
TESTS = tests/all.test
|
||||
TEST_LOG_COMPILER = ${srcdir}/tests/run
|
||||
TESTS = tests/mantest tests/jqtest tests/onigtest tests/shtest
|
||||
TESTS_ENVIRONMENT = NO_VALGRIND=$(NO_VALGRIND)
|
||||
|
||||
|
||||
### Building the manpage
|
||||
@@ -56,20 +108,23 @@ man_MANS = jq.1
|
||||
.PHONY: real_docs
|
||||
if ENABLE_DOCS
|
||||
jq.1: $(srcdir)/docs/content/3.manual/manual.yml
|
||||
( cd ${abs_srcdir}/docs; '$(BUNDLER)' exec rake manpage ) > $@ || { rm -f $@; false; }
|
||||
$(AM_V_GEN) ( cd ${abs_srcdir}/docs; '$(BUNDLER)' exec rake manpage ) > $@ || { rm -f $@; false; }
|
||||
jq.1.prebuilt: jq.1
|
||||
$(AM_V_GEN) cp $^ $@ || { rm -f $@; false; }
|
||||
jq.1.default: $(srcdir)/docs/default_manpage.md
|
||||
( cd ${abs_srcdir}/docs; '$(BUNDLER)' exec rake manpage_default ) > $@ || { rm -f $@; false; }
|
||||
real_docs: jq.1
|
||||
if cmp jq.1 $(srcdir)/jq.1.default > /dev/null; then\
|
||||
$(AM_V_GEN) ( cd ${abs_srcdir}/docs; '$(BUNDLER)' exec rake manpage_default ) > $@ || { rm -f $@; false; }
|
||||
real_docs: jq.1 jq.1.prebuilt
|
||||
@if cmp jq.1 $(srcdir)/jq.1.default > /dev/null; then\
|
||||
rm -f jq.1; $(MAKE) $(AM_MAKEFLAGS) jq.1;\
|
||||
fi
|
||||
else
|
||||
jq.1: $(srcdir)/jq.1.default
|
||||
cp $^ $@
|
||||
jq.1: $(srcdir)/jq.1.prebuilt
|
||||
$(AM_V_GEN) cp $^ $@
|
||||
real_docs:
|
||||
@echo "Ruby dependencies not found, cannot build manpage." > /dev/stderr
|
||||
@echo "Follow the instructions in docs/README.md to install them" > /dev/stderr
|
||||
@echo "and then rerun ./configure" > /dev/stderr
|
||||
@echo "A pre-built, possibly out-of-date manpage used." > /dev/stderr
|
||||
false
|
||||
endif
|
||||
|
||||
@@ -93,24 +148,31 @@ DOC_FILES = docs/content docs/public docs/templates docs/site.yml \
|
||||
# setup is only used by distribution developers, not package developers.
|
||||
# Still, as a matter of allowing patching, its not a bad idea to distribute
|
||||
# the developer setup script in the tarball.
|
||||
EXTRA_DIST = config.h.in $(BUILT_SOURCES) $(man_MANS) $(TESTS) \
|
||||
$(TEST_LOG_COMPILER) gen_utf8_tables.py jq.spec \
|
||||
$(DOC_FILES)
|
||||
EXTRA_DIST = $(man_MANS) $(TESTS) $(TEST_LOG_COMPILER) jq.spec \
|
||||
$(DOC_FILES) scripts/version parser.h parser.c lexer.h \
|
||||
lexer.c tests/onig.supp tests/torture/input0.json \
|
||||
tests/modules/.jq tests/modules/a.jq tests/modules/b/b.jq \
|
||||
tests/modules/c/c.jq tests/modules/c/d.jq \
|
||||
tests/modules/lib/jq/e/e.jq tests/modules/lib/jq/f.jq \
|
||||
tests/modules/data.json \
|
||||
tests/modules/syntaxerror/syntaxerror.jq \
|
||||
tests/modules/test_bind_order.jq \
|
||||
tests/modules/test_bind_order0.jq \
|
||||
tests/modules/test_bind_order1.jq \
|
||||
tests/modules/test_bind_order2.jq
|
||||
|
||||
|
||||
# README.md is expected in Github projects, good stuff in it, so we'll
|
||||
# distribute it and install it with the package in the doc directory.
|
||||
docdir = ${datadir}/doc/${PACKAGE}
|
||||
dist_doc_DATA = README.md COPYING AUTHORS README
|
||||
|
||||
releasetag:
|
||||
git tag -s "jq-$$(cat VERSION)" -m "jq release $$(cat VERSION)"
|
||||
|
||||
RELEASE ?= 1
|
||||
rpm: jq
|
||||
rpm: dist jq.spec
|
||||
@echo "Packaging jq as an RPM ..."
|
||||
mkdir -p rpm/SOURCES rpm/BUILD rpm/BUILDROOT rpm/RPMS rpm/SPECS
|
||||
cp jq-$$(cat VERSION).tar.gz rpm/SOURCES/
|
||||
rpmbuild -tb --define "_topdir ${PWD}/rpm" --define "_prefix /usr" --define "myver $$(cat VERSION)" --define "myrel ${RELEASE}" rpm/SOURCES/jq-$$(cat VERSION).tar.gz
|
||||
cp jq-$(VERSION).tar.gz rpm/SOURCES/
|
||||
rpmbuild -tb --define "_topdir ${PWD}/rpm" --define "_prefix /usr" --define "myver $(VERSION)" --define "myrel ${RELEASE}" rpm/SOURCES/jq-$(VERSION).tar.gz
|
||||
find rpm/RPMS/ -name "*.rpm" -exec mv {} ./ \;
|
||||
rm -rf rpm
|
||||
|
||||
@@ -119,5 +181,16 @@ dist-clean-local:
|
||||
|
||||
# Not sure why this doesn't get cleaned up automatically, guess
|
||||
# automake used to man pages which are hand coded?
|
||||
clean-local:
|
||||
# 'make clean' doesn't delete the manpage if it can't be rebuilt
|
||||
clean-local-docs:
|
||||
if ENABLE_DOCS
|
||||
rm -f jq.1
|
||||
endif
|
||||
|
||||
clean-local-gcov:
|
||||
rm -f *.gcno *.gcda *.gcov
|
||||
|
||||
clean-local: clean-local-docs clean-local-gcov
|
||||
rm -f version.h .remake-version-h
|
||||
|
||||
.PHONY: clean-local-docs clean-local-gcov
|
||||
@@ -0,0 +1,88 @@
|
||||
|
||||
Release history
|
||||
|
||||
* jq version 1.0 was released on Sun Oct 21 2012
|
||||
* jq version 1.1 was released on Sun Oct 21 2012
|
||||
* jq version 1.2 was released on Thu Dec 20 2012
|
||||
* jq version 1.3 was released on Sun May 19 2013
|
||||
* jq version 1.4 was released on Mon Jun 9 2014
|
||||
|
||||
New features in 1.5 since 1.4:
|
||||
|
||||
- regular expressions (with Oniguruma)
|
||||
|
||||
- a library/module system
|
||||
|
||||
- many new builtins
|
||||
|
||||
- datetime builtins
|
||||
- math builtins
|
||||
- regexp-related builtins
|
||||
- stream-related builtins (e.g., all/1, any/1)
|
||||
- minimal I/O builtins (`inputs`, `debug`)
|
||||
|
||||
- new syntactic features, including:
|
||||
|
||||
- destructuring (`. as [$first, $second] | ...`)
|
||||
- try/catch, generalized `?` operator, and label/break
|
||||
- `foreach`
|
||||
- multiple definitions of a function with different numbers of
|
||||
arguments
|
||||
|
||||
- command-line arguments
|
||||
|
||||
- --join-lines / -j for raw output
|
||||
- --argjson and --slurpfile
|
||||
- --tab and --indent
|
||||
- --stream (streaming JSON parser)
|
||||
- --seq (RFC7464 JSON text sequence)
|
||||
- --run-tests improvements
|
||||
|
||||
- optimizations:
|
||||
|
||||
- tail-call optimization
|
||||
- reduce and foreach no longer leak a reference to .
|
||||
|
||||
New features in 1.4 since 1.3:
|
||||
|
||||
- command-line arguments
|
||||
|
||||
- jq --arg-file variable file
|
||||
- jq --unbuffered
|
||||
- jq -e / --exit-status (set exit status based on outputs)
|
||||
- jq -S / --sort-keys (now jq no longer sorts object keys by
|
||||
default
|
||||
|
||||
- syntax
|
||||
|
||||
- .. -> like // in XPath (recursive traversal)
|
||||
- question mark (e.g., .a?) to suppress errors
|
||||
- ."foo" syntax (equivalent to .["foo"])
|
||||
- better error handling for .foo
|
||||
- added % operator (modulo)
|
||||
- allow negation without requiring extra parenthesis
|
||||
- more function arguments (up to six)
|
||||
|
||||
- filters:
|
||||
|
||||
- any, all
|
||||
- iterables, arrays, objects, scalars, nulls, booleans, numbers,
|
||||
strings, values
|
||||
|
||||
- string built-ins:
|
||||
|
||||
- split
|
||||
- join (join an array of strings with a given separator string)
|
||||
- ltrimstr, rtrimstr
|
||||
- startswith, endswith
|
||||
- explode, implode
|
||||
- fromjson, tojson
|
||||
- index, rindex, indices
|
||||
|
||||
- math functions
|
||||
|
||||
- floor, sqrt, cbrt, etetera (depends on what's available from libm)
|
||||
|
||||
- libjq -- a C API interface to jq's JSON representation and for
|
||||
running jq programs from C applications
|
||||
|
||||
@@ -4,20 +4,61 @@ jq
|
||||
jq is a command-line JSON processor.
|
||||
|
||||
If you want to learn to use jq, read the documentation at
|
||||
[http://stedolan.github.io/jq](http://stedolan.github.io/jq). This
|
||||
[http://stedolan.github.io/jq](http://stedolan.github.io/jq). This
|
||||
documentation is generated from the docs/ folder of this repository.
|
||||
You can also try it online at [jqplay.org](http://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: http://github.com/stedolan/jq/wiki
|
||||
|
||||
To build jq, run
|
||||
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:
|
||||
|
||||
autoreconf -i # if building from git
|
||||
./configure
|
||||
make
|
||||
sudo make install (optionally)
|
||||
make -j8
|
||||
make check
|
||||
|
||||
If you've just checked out the latest version from git (rather than
|
||||
using a released source tarball) then you'll need to run this first:
|
||||
To build without bison or flex, add `--disable-maintainer-mode` to the
|
||||
./configure invocation:
|
||||
|
||||
autoreconf
|
||||
./configure --disable-maintainer-mode
|
||||
|
||||
(Developers must not use `--disable-maintainer-mode`, not when making
|
||||
changes to the jq parser and/or lexer.)
|
||||
|
||||
To build a statically linked version of jq, run:
|
||||
|
||||
make LDFLAGS=-all-static
|
||||
|
||||
After make finishes, you'll be able to use `./jq`. You can also
|
||||
install it using:
|
||||
|
||||
sudo make install
|
||||
|
||||
If you're not using the latest git version but instead building a
|
||||
released tarball (available on the website), then you won't need to
|
||||
run `autoreconf` (and shouldn't), and you won't need flex or bison.
|
||||
|
||||
To cross-compile for OS X and Windows, see docs/Rakefile's build task
|
||||
and scripts/crosscompile. You'll need a cross-compilation environment,
|
||||
such as Mingw for cross-compiling for Windows.
|
||||
|
||||
Cross-compilation requires a clean workspace, then:
|
||||
|
||||
# git clean ...
|
||||
autoreconf -i
|
||||
./configure
|
||||
make distclean
|
||||
scripts/crosscompile <name-of-build> <configure-options>
|
||||
|
||||
Use the --host= and --target= ./configure options to select a
|
||||
cross-compilation environment. See also the wiki.
|
||||
|
||||
Send questions to http://stackoverflow.com/questions/tagged/jq or to the #jq channel (http://irc.lc/freenode/%23jq/) on Freenode (https://webchat.freenode.net/).
|
||||
@@ -1,21 +1,10 @@
|
||||
#ifndef BUILTIN_H
|
||||
#define BUILTIN_H
|
||||
|
||||
#include "jq.h"
|
||||
#include "bytecode.h"
|
||||
#include "compile.h"
|
||||
|
||||
block builtins_bind(block);
|
||||
|
||||
|
||||
typedef void (*cfunction_ptr)(void);
|
||||
|
||||
struct cfunction {
|
||||
cfunction_ptr fptr;
|
||||
const char* name;
|
||||
int nargs;
|
||||
};
|
||||
|
||||
|
||||
jv cfunction_invoke(struct cfunction* function, jv input[]);
|
||||
|
||||
int builtins_bind(jq_state *, block*);
|
||||
|
||||
#endif
|
||||
+58
-25
@@ -3,17 +3,64 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "bytecode.h"
|
||||
#include "opcode.h"
|
||||
#include "jv_alloc.h"
|
||||
|
||||
static int bytecode_operation_length(uint16_t* codeptr) {
|
||||
// flags, length
|
||||
#define NONE 0, 1
|
||||
#define CONSTANT OP_HAS_CONSTANT, 2
|
||||
#define VARIABLE (OP_HAS_VARIABLE | OP_HAS_BINDING), 3
|
||||
#define GLOBAL (OP_HAS_CONSTANT | OP_HAS_VARIABLE | OP_HAS_BINDING | OP_IS_CALL_PSEUDO), 4
|
||||
#define BRANCH OP_HAS_BRANCH, 2
|
||||
#define CFUNC (OP_HAS_CFUNC | OP_HAS_BINDING), 3
|
||||
#define UFUNC (OP_HAS_UFUNC | OP_HAS_BINDING | OP_IS_CALL_PSEUDO), 4
|
||||
#define DEFINITION (OP_IS_CALL_PSEUDO | OP_HAS_BINDING), 0
|
||||
#define CLOSURE_REF_IMM (OP_IS_CALL_PSEUDO | OP_HAS_BINDING), 2
|
||||
|
||||
#define OP(name, imm, in, out) \
|
||||
{name, #name, imm, in, out},
|
||||
|
||||
static const struct opcode_description opcode_descriptions[] = {
|
||||
#include "opcode_list.h"
|
||||
};
|
||||
|
||||
static const struct opcode_description invalid_opcode_description = {
|
||||
-1, "#INVALID", 0, 0, 0, 0
|
||||
};
|
||||
|
||||
|
||||
const struct opcode_description* opcode_describe(opcode op) {
|
||||
if ((int)op >= 0 && (int)op < NUM_OPCODES) {
|
||||
return &opcode_descriptions[op];
|
||||
} else {
|
||||
return &invalid_opcode_description;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int bytecode_operation_length(uint16_t* codeptr) {
|
||||
int length = opcode_describe(*codeptr)->length;
|
||||
if (*codeptr == CALL_JQ) {
|
||||
if (*codeptr == CALL_JQ || *codeptr == TAIL_CALL_JQ) {
|
||||
length += codeptr[1] * 2;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
static void dump_code(int indent, struct bytecode* bc) {
|
||||
int pc = 0;
|
||||
while (pc < bc->codelen) {
|
||||
printf("%*s", indent, "");
|
||||
dump_operation(bc, bc->code + pc);
|
||||
printf("\n");
|
||||
pc += bytecode_operation_length(bc->code + pc);
|
||||
}
|
||||
}
|
||||
|
||||
static void symbol_table_free(struct symbol_table* syms) {
|
||||
jv_mem_free(syms->cfunctions);
|
||||
jv_free(syms->cfunc_names);
|
||||
jv_mem_free(syms);
|
||||
}
|
||||
|
||||
void dump_disassembly(int indent, struct bytecode* bc) {
|
||||
if (bc->nclosures > 0) {
|
||||
printf("%*s[params: ", indent, "");
|
||||
@@ -37,16 +84,6 @@ void dump_disassembly(int indent, struct bytecode* bc) {
|
||||
}
|
||||
}
|
||||
|
||||
void dump_code(int indent, struct bytecode* bc) {
|
||||
int pc = 0;
|
||||
while (pc < bc->codelen) {
|
||||
printf("%*s", indent, "");
|
||||
dump_operation(bc, bc->code + pc);
|
||||
printf("\n");
|
||||
pc += bytecode_operation_length(bc->code + pc);
|
||||
}
|
||||
}
|
||||
|
||||
static struct bytecode* getlevel(struct bytecode* bc, int level) {
|
||||
while (level > 0) {
|
||||
bc = bc->parent;
|
||||
@@ -62,17 +99,17 @@ void dump_operation(struct bytecode* bc, uint16_t* codeptr) {
|
||||
printf("%s", op->name);
|
||||
if (op->length > 1) {
|
||||
uint16_t imm = bc->code[pc++];
|
||||
if (op->op == CALL_JQ) {
|
||||
if (op->op == CALL_JQ || op->op == TAIL_CALL_JQ) {
|
||||
for (int i=0; i<imm+1; i++) {
|
||||
uint16_t level = bc->code[pc++];
|
||||
uint16_t idx = bc->code[pc++];
|
||||
jv name;
|
||||
if (idx & ARG_NEWCLOSURE) {
|
||||
idx &= ~ARG_NEWCLOSURE;
|
||||
name = jv_object_get(jv_copy(getlevel(bc,level)->subfunctions[idx]->debuginfo),
|
||||
name = jv_object_get(jv_copy(getlevel(bc,level)->subfunctions[idx]->debuginfo),
|
||||
jv_string("name"));
|
||||
} else {
|
||||
name = jv_array_get(jv_object_get(jv_copy(getlevel(bc,level)->debuginfo),
|
||||
name = jv_array_get(jv_object_get(jv_copy(getlevel(bc,level)->debuginfo),
|
||||
jv_string("params")), idx);
|
||||
}
|
||||
printf(" %s:%d",
|
||||
@@ -96,8 +133,8 @@ void dump_operation(struct bytecode* bc, uint16_t* codeptr) {
|
||||
} else if (op->flags & OP_HAS_VARIABLE) {
|
||||
uint16_t v = bc->code[pc++];
|
||||
jv name = jv_array_get(jv_object_get(jv_copy(getlevel(bc,imm)->debuginfo), jv_string("locals")), v);
|
||||
printf(" $%s:%d",
|
||||
jv_string_value(name),
|
||||
printf(" $%s:%d",
|
||||
jv_string_value(name),
|
||||
v);
|
||||
jv_free(name);
|
||||
if (imm) {
|
||||
@@ -106,16 +143,12 @@ void dump_operation(struct bytecode* bc, uint16_t* codeptr) {
|
||||
} else {
|
||||
printf(" %d", imm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void symbol_table_free(struct symbol_table* syms) {
|
||||
jv_mem_free(syms->cfunctions);
|
||||
jv_free(syms->cfunc_names);
|
||||
jv_mem_free(syms);
|
||||
}
|
||||
}
|
||||
|
||||
void bytecode_free(struct bytecode* bc) {
|
||||
if (!bc)
|
||||
return;
|
||||
jv_mem_free(bc->code);
|
||||
jv_free(bc->constants);
|
||||
for (int i=0; i<bc->nsubfunctions; i++)
|
||||
|
||||
+48
-4
@@ -3,10 +3,55 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "jv.h"
|
||||
#include "opcode.h"
|
||||
#include "builtin.h"
|
||||
|
||||
typedef enum {
|
||||
#define OP(name, imm, in, out) name,
|
||||
#include "opcode_list.h"
|
||||
#undef OP
|
||||
} opcode;
|
||||
|
||||
enum {
|
||||
NUM_OPCODES =
|
||||
#define OP(name, imm, in, out) +1
|
||||
#include "opcode_list.h"
|
||||
#undef OP
|
||||
};
|
||||
|
||||
enum {
|
||||
OP_HAS_CONSTANT = 2,
|
||||
OP_HAS_VARIABLE = 4,
|
||||
OP_HAS_BRANCH = 8,
|
||||
OP_HAS_CFUNC = 32,
|
||||
OP_HAS_UFUNC = 64,
|
||||
OP_IS_CALL_PSEUDO = 128,
|
||||
OP_HAS_BINDING = 1024,
|
||||
// NOTE: Not actually part of any op -- a pseudo-op flag for special
|
||||
// handling of `break`.
|
||||
OP_BIND_WILDCARD = 2048,
|
||||
};
|
||||
struct opcode_description {
|
||||
opcode op;
|
||||
const char* name;
|
||||
|
||||
int flags;
|
||||
|
||||
// length in 16-bit units
|
||||
int length;
|
||||
|
||||
int stack_in, stack_out;
|
||||
};
|
||||
|
||||
const struct opcode_description* opcode_describe(opcode op);
|
||||
|
||||
|
||||
#define MAX_CFUNCTION_ARGS 10
|
||||
typedef void (*cfunction_ptr)();
|
||||
struct cfunction {
|
||||
cfunction_ptr fptr;
|
||||
const char* name;
|
||||
int nargs;
|
||||
};
|
||||
|
||||
struct symbol_table {
|
||||
struct cfunction* cfunctions;
|
||||
int ncfunctions;
|
||||
@@ -39,10 +84,9 @@ struct bytecode {
|
||||
};
|
||||
|
||||
void dump_disassembly(int, struct bytecode* code);
|
||||
void dump_code(int, struct bytecode* code);
|
||||
void dump_operation(struct bytecode* bc, uint16_t* op);
|
||||
|
||||
void symbol_table_free(struct symbol_table* syms);
|
||||
int bytecode_operation_length(uint16_t* codeptr);
|
||||
void bytecode_free(struct bytecode* bc);
|
||||
|
||||
#endif
|
||||
Executable
+102
@@ -0,0 +1,102 @@
|
||||
#!/bin/bash
|
||||
|
||||
# old values
|
||||
OLD_CFLAGS=$CFLAGS
|
||||
OLD_LDFLAGS=$LDFLAGS
|
||||
ORIG_PWD=`pwd`
|
||||
|
||||
# For parallelism in make
|
||||
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"
|
||||
curl http://www.geocities.jp/kosako3/oniguruma/archive/onig-5.9.5.tar.gz | tar xz
|
||||
cd onig-5.9.5
|
||||
|
||||
# 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
|
||||
# NEWS and ChangeLog.
|
||||
touch NEWS ChangeLog
|
||||
autoreconf -fi >/dev/null 2>&1
|
||||
|
||||
|
||||
|
||||
|
||||
CC=`xcrun -f clang`
|
||||
cd $ORIG_PWD
|
||||
|
||||
autoreconf -fi
|
||||
for arch in i386 x86_64 armv7 armv7s arm64; do
|
||||
|
||||
# Some of the architectures are a bit different...
|
||||
if [[ "$arch" = "i386" || "$arch" = "x86_64" ]]
|
||||
then
|
||||
SYSROOT=`xcrun -f --sdk iphonesimulator --show-sdk-path`
|
||||
else
|
||||
SYSROOT=`xcrun -f --sdk iphoneos --show-sdk-path`
|
||||
fi
|
||||
if [[ "$arch" = "arm64" ]]
|
||||
then
|
||||
HOST="aarch64-apple-darwin"
|
||||
else
|
||||
HOST="$arch-apple-darwin"
|
||||
fi
|
||||
|
||||
CFLAGS="-arch $arch -miphoneos-version-min=6.0 -isysroot $SYSROOT $OLD_CFLAGS"
|
||||
LDFLAGS="-arch $arch -miphoneos-version-min=6.0 -isysroot $SYSROOT $OLD_LDFLAGS"
|
||||
|
||||
|
||||
|
||||
# Build oniguruma for this architecture
|
||||
cd onig-5.9.5
|
||||
CC=$CC CFLAGS=$CFLAGS LDFLAGS=$LDFLAGS ./configure --disable-shared --enable-static --host=$HOST --prefix=$ORIG_PWD/build/ios/$arch
|
||||
STATUS=$?
|
||||
if [ $STATUS -ne 0 ]
|
||||
then
|
||||
echo "Failed to configure oniguruma for architecture $arch. Check `pwd`/config.log for details."
|
||||
cd $PWD
|
||||
exit $STATUS
|
||||
fi
|
||||
make clean
|
||||
make $NJBOS install
|
||||
STATUS=$?
|
||||
if [ $STATUS -ne 0 ]
|
||||
then
|
||||
echo "Failed to make oniguruma for architecture $arch."
|
||||
cd $PWD
|
||||
exit $STATUS
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# Build jq for this architecture
|
||||
cd $ORIG_PWD
|
||||
CC=$CC CFLAGS=$CFLAGS LDFLAGS=$LDFLAGS ./configure --disable-shared --enable-static -host=$HOST --prefix=$ORIG_PWD/build/ios/$arch --with-oniguruma=$ORIG_PWD/build/ios/$arch
|
||||
STATUS=$?
|
||||
if [ $STATUS -ne 0 ]
|
||||
then
|
||||
echo "Failed to configure jq for architecture $arch"
|
||||
exit $STATUS
|
||||
fi
|
||||
make clean
|
||||
make $NJOBS install
|
||||
STATUS=$?
|
||||
if [ $STATUS -ne 0 ]
|
||||
then
|
||||
echo "Failed to make jq for architecture $arch"
|
||||
exit $STATUS
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
# lipo together the different architectures into a universal 'fat' file
|
||||
lipo -create -output $ORIG_PWD/build/ios/libonig.a $ORIG_PWD/build/ios/{i386,x86_64,armv7,armv7s,arm64}/lib/libonig.a
|
||||
lipo -create -output $ORIG_PWD/build/ios/libjq.a $ORIG_PWD/build/ios/{i386,x86_64,armv7,armv7s,arm64}/lib/libjq.a
|
||||
|
||||
# copy the products into the destination directory and clean up the single-architecture files.
|
||||
cp $ORIG_PWD/build/ios/i386/include/*.h $ORIG_PWD/build/ios/
|
||||
rm -rf $ORIG_PWD/build/ios/{i386,x86_64,armv7,armv7s,arm64}
|
||||
|
||||
echo "Products are in $ORIG_PWD/build/ios"
|
||||
@@ -2,13 +2,9 @@
|
||||
#define COMPILE_H
|
||||
#include <stdint.h>
|
||||
#include "jv.h"
|
||||
#include "opcode.h"
|
||||
#include "bytecode.h"
|
||||
#include "locfile.h"
|
||||
|
||||
struct bytecode;
|
||||
struct symbol_table;
|
||||
struct cfunction;
|
||||
|
||||
struct inst;
|
||||
typedef struct inst inst;
|
||||
|
||||
@@ -18,41 +14,68 @@ typedef struct block {
|
||||
inst* last;
|
||||
} block;
|
||||
|
||||
block gen_location(location, block);
|
||||
block gen_location(location, struct locfile*, block);
|
||||
|
||||
block gen_noop();
|
||||
int block_is_noop(block b);
|
||||
block gen_op_simple(opcode op);
|
||||
block gen_const(jv constant);
|
||||
block gen_const_global(jv constant, const char *name);
|
||||
int block_is_const(block b);
|
||||
int block_is_const_inf(block b);
|
||||
jv_kind block_const_kind(block b);
|
||||
jv block_const(block b);
|
||||
block gen_op_target(opcode op, block target);
|
||||
block gen_op_var_unbound(opcode op, const char* name);
|
||||
block gen_op_var_bound(opcode op, block binder);
|
||||
block gen_op_block_unbound(opcode op, const char* name);
|
||||
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_module(block metadata);
|
||||
jv block_module_meta(block b);
|
||||
block gen_import(const char* name, block metadata, const char *as, int is_data);
|
||||
block gen_function(const char* name, block formals, block body);
|
||||
block gen_param_regular(const char* name);
|
||||
block gen_param(const char* name);
|
||||
block gen_lambda(block body);
|
||||
block gen_call(const char* name, block body);
|
||||
block gen_subexp(block a);
|
||||
block gen_both(block a, block b);
|
||||
block gen_const_object(block expr);
|
||||
block gen_collect(block expr);
|
||||
block gen_reduce(const char* varname, block source, block init, block body);
|
||||
block gen_reduce(block source, block matcher, block init, block body);
|
||||
block gen_foreach(block source, block matcher, block init, block update, block extract);
|
||||
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_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_cond(block cond, block iftrue, block iffalse);
|
||||
block gen_try_handler(block handler);
|
||||
block gen_try(block exp, block handler);
|
||||
block gen_label(const char *label, block exp);
|
||||
|
||||
block gen_cbinding(const struct cfunction* functions, int nfunctions, block b);
|
||||
|
||||
void block_append(block* b, block b2);
|
||||
block block_join(block a, block b);
|
||||
int block_has_only_binders_and_imports(block, int bindflags);
|
||||
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_drop_unreferenced(block body);
|
||||
|
||||
int block_compile(block, struct locfile*, struct bytecode**);
|
||||
jv block_take_imports(block* body);
|
||||
|
||||
int block_compile(block, struct bytecode**, struct locfile*);
|
||||
|
||||
void block_free(block);
|
||||
|
||||
@@ -68,10 +91,11 @@ void block_free(block);
|
||||
#define BLOCK_5(b1,b2,b3,b4,b5) (block_join(BLOCK_4(b1,b2,b3,b4),(b5)))
|
||||
#define BLOCK_6(b1,b2,b3,b4,b5,b6) (block_join(BLOCK_5(b1,b2,b3,b4,b5),(b6)))
|
||||
#define BLOCK_7(b1,b2,b3,b4,b5,b6,b7) (block_join(BLOCK_6(b1,b2,b3,b4,b5,b6),(b7)))
|
||||
#define BLOCK_8(b1,b2,b3,b4,b5,b6,b7,b8) (block_join(BLOCK_7(b1,b2,b3,b4,b5,b6,b7),(b8)))
|
||||
|
||||
#define BLOCK_IDX(_1,_2,_3,_4,_5,_6,_7,NAME,...) NAME
|
||||
#define BLOCK_IDX(_1,_2,_3,_4,_5,_6,_7,_8,NAME,...) NAME
|
||||
#define BLOCK(...) \
|
||||
BLOCK_IDX(__VA_ARGS__, BLOCK_7, BLOCK_6, BLOCK_5, BLOCK_4, BLOCK_3, BLOCK_2, BLOCK_1)(__VA_ARGS__)
|
||||
BLOCK_IDX(__VA_ARGS__, BLOCK_8, BLOCK_7, BLOCK_6, BLOCK_5, BLOCK_4, BLOCK_3, BLOCK_2, BLOCK_1)(__VA_ARGS__)
|
||||
|
||||
|
||||
#endif
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* Define to 1 if your C compiler doesn't accept -c and -o together. */
|
||||
#undef NO_MINUS_C_MINUS_O
|
||||
|
||||
/* Name of package */
|
||||
#undef PACKAGE
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#undef PACKAGE_BUGREPORT
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#undef PACKAGE_NAME
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#undef PACKAGE_STRING
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#undef PACKAGE_TARNAME
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#undef PACKAGE_URL
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
/* Version number of package */
|
||||
#undef VERSION
|
||||
|
||||
/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a
|
||||
`char[]'. */
|
||||
#undef YYTEXT_POINTER
|
||||
@@ -0,0 +1,10 @@
|
||||
config.guess
|
||||
config.sub
|
||||
compile
|
||||
depcomp
|
||||
install-sh
|
||||
ltmain.sh
|
||||
missing
|
||||
ylwrap
|
||||
test-driver
|
||||
ar-lib
|
||||
-343
@@ -1,343 +0,0 @@
|
||||
#! /bin/sh
|
||||
# Wrapper for compilers which do not understand '-c -o'.
|
||||
|
||||
scriptversion=2012-03-05.13; # UTC
|
||||
|
||||
# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2009, 2010, 2012 Free
|
||||
# Software Foundation, Inc.
|
||||
# Written by Tom Tromey <tromey@cygnus.com>.
|
||||
#
|
||||
# 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
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# This file is maintained in Automake, please report
|
||||
# bugs to <bug-automake@gnu.org> or send patches to
|
||||
# <automake-patches@gnu.org>.
|
||||
|
||||
nl='
|
||||
'
|
||||
|
||||
# We need space, tab and new line, in precisely that order. Quoting is
|
||||
# there to prevent tools from complaining about whitespace usage.
|
||||
IFS=" "" $nl"
|
||||
|
||||
file_conv=
|
||||
|
||||
# func_file_conv build_file lazy
|
||||
# Convert a $build file to $host form and store it in $file
|
||||
# Currently only supports Windows hosts. If the determined conversion
|
||||
# type is listed in (the comma separated) LAZY, no conversion will
|
||||
# take place.
|
||||
func_file_conv ()
|
||||
{
|
||||
file=$1
|
||||
case $file in
|
||||
/ | /[!/]*) # absolute file, and not a UNC file
|
||||
if test -z "$file_conv"; then
|
||||
# lazily determine how to convert abs files
|
||||
case `uname -s` in
|
||||
MINGW*)
|
||||
file_conv=mingw
|
||||
;;
|
||||
CYGWIN*)
|
||||
file_conv=cygwin
|
||||
;;
|
||||
*)
|
||||
file_conv=wine
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
case $file_conv/,$2, in
|
||||
*,$file_conv,*)
|
||||
;;
|
||||
mingw/*)
|
||||
file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
|
||||
;;
|
||||
cygwin/*)
|
||||
file=`cygpath -m "$file" || echo "$file"`
|
||||
;;
|
||||
wine/*)
|
||||
file=`winepath -w "$file" || echo "$file"`
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# func_cl_dashL linkdir
|
||||
# Make cl look for libraries in LINKDIR
|
||||
func_cl_dashL ()
|
||||
{
|
||||
func_file_conv "$1"
|
||||
if test -z "$lib_path"; then
|
||||
lib_path=$file
|
||||
else
|
||||
lib_path="$lib_path;$file"
|
||||
fi
|
||||
linker_opts="$linker_opts -LIBPATH:$file"
|
||||
}
|
||||
|
||||
# func_cl_dashl library
|
||||
# Do a library search-path lookup for cl
|
||||
func_cl_dashl ()
|
||||
{
|
||||
lib=$1
|
||||
found=no
|
||||
save_IFS=$IFS
|
||||
IFS=';'
|
||||
for dir in $lib_path $LIB
|
||||
do
|
||||
IFS=$save_IFS
|
||||
if $shared && test -f "$dir/$lib.dll.lib"; then
|
||||
found=yes
|
||||
lib=$dir/$lib.dll.lib
|
||||
break
|
||||
fi
|
||||
if test -f "$dir/$lib.lib"; then
|
||||
found=yes
|
||||
lib=$dir/$lib.lib
|
||||
break
|
||||
fi
|
||||
done
|
||||
IFS=$save_IFS
|
||||
|
||||
if test "$found" != yes; then
|
||||
lib=$lib.lib
|
||||
fi
|
||||
}
|
||||
|
||||
# func_cl_wrapper cl arg...
|
||||
# Adjust compile command to suit cl
|
||||
func_cl_wrapper ()
|
||||
{
|
||||
# Assume a capable shell
|
||||
lib_path=
|
||||
shared=:
|
||||
linker_opts=
|
||||
for arg
|
||||
do
|
||||
if test -n "$eat"; then
|
||||
eat=
|
||||
else
|
||||
case $1 in
|
||||
-o)
|
||||
# configure might choose to run compile as 'compile cc -o foo foo.c'.
|
||||
eat=1
|
||||
case $2 in
|
||||
*.o | *.[oO][bB][jJ])
|
||||
func_file_conv "$2"
|
||||
set x "$@" -Fo"$file"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
func_file_conv "$2"
|
||||
set x "$@" -Fe"$file"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
-I)
|
||||
eat=1
|
||||
func_file_conv "$2" mingw
|
||||
set x "$@" -I"$file"
|
||||
shift
|
||||
;;
|
||||
-I*)
|
||||
func_file_conv "${1#-I}" mingw
|
||||
set x "$@" -I"$file"
|
||||
shift
|
||||
;;
|
||||
-l)
|
||||
eat=1
|
||||
func_cl_dashl "$2"
|
||||
set x "$@" "$lib"
|
||||
shift
|
||||
;;
|
||||
-l*)
|
||||
func_cl_dashl "${1#-l}"
|
||||
set x "$@" "$lib"
|
||||
shift
|
||||
;;
|
||||
-L)
|
||||
eat=1
|
||||
func_cl_dashL "$2"
|
||||
;;
|
||||
-L*)
|
||||
func_cl_dashL "${1#-L}"
|
||||
;;
|
||||
-static)
|
||||
shared=false
|
||||
;;
|
||||
-Wl,*)
|
||||
arg=${1#-Wl,}
|
||||
save_ifs="$IFS"; IFS=','
|
||||
for flag in $arg; do
|
||||
IFS="$save_ifs"
|
||||
linker_opts="$linker_opts $flag"
|
||||
done
|
||||
IFS="$save_ifs"
|
||||
;;
|
||||
-Xlinker)
|
||||
eat=1
|
||||
linker_opts="$linker_opts $2"
|
||||
;;
|
||||
-*)
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
*.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
|
||||
func_file_conv "$1"
|
||||
set x "$@" -Tp"$file"
|
||||
shift
|
||||
;;
|
||||
*.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
|
||||
func_file_conv "$1" mingw
|
||||
set x "$@" "$file"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
shift
|
||||
done
|
||||
if test -n "$linker_opts"; then
|
||||
linker_opts="-link$linker_opts"
|
||||
fi
|
||||
exec "$@" $linker_opts
|
||||
exit 1
|
||||
}
|
||||
|
||||
eat=
|
||||
|
||||
case $1 in
|
||||
'')
|
||||
echo "$0: No command. Try '$0 --help' for more information." 1>&2
|
||||
exit 1;
|
||||
;;
|
||||
-h | --h*)
|
||||
cat <<\EOF
|
||||
Usage: compile [--help] [--version] PROGRAM [ARGS]
|
||||
|
||||
Wrapper for compilers which do not understand '-c -o'.
|
||||
Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
|
||||
arguments, and rename the output as expected.
|
||||
|
||||
If you are trying to build a whole package this is not the
|
||||
right script to run: please start by reading the file 'INSTALL'.
|
||||
|
||||
Report bugs to <bug-automake@gnu.org>.
|
||||
EOF
|
||||
exit $?
|
||||
;;
|
||||
-v | --v*)
|
||||
echo "compile $scriptversion"
|
||||
exit $?
|
||||
;;
|
||||
cl | *[/\\]cl | cl.exe | *[/\\]cl.exe )
|
||||
func_cl_wrapper "$@" # Doesn't return...
|
||||
;;
|
||||
esac
|
||||
|
||||
ofile=
|
||||
cfile=
|
||||
|
||||
for arg
|
||||
do
|
||||
if test -n "$eat"; then
|
||||
eat=
|
||||
else
|
||||
case $1 in
|
||||
-o)
|
||||
# configure might choose to run compile as 'compile cc -o foo foo.c'.
|
||||
# So we strip '-o arg' only if arg is an object.
|
||||
eat=1
|
||||
case $2 in
|
||||
*.o | *.obj)
|
||||
ofile=$2
|
||||
;;
|
||||
*)
|
||||
set x "$@" -o "$2"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*.c)
|
||||
cfile=$1
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
shift
|
||||
done
|
||||
|
||||
if test -z "$ofile" || test -z "$cfile"; then
|
||||
# If no '-o' option was seen then we might have been invoked from a
|
||||
# pattern rule where we don't need one. That is ok -- this is a
|
||||
# normal compilation that the losing compiler can handle. If no
|
||||
# '.c' file was seen then we are probably linking. That is also
|
||||
# ok.
|
||||
exec "$@"
|
||||
fi
|
||||
|
||||
# Name of file we expect compiler to create.
|
||||
cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
|
||||
|
||||
# Create the lock directory.
|
||||
# Note: use '[/\\:.-]' here to ensure that we don't use the same name
|
||||
# that we are using for the .o file. Also, base the name on the expected
|
||||
# object file name, since that is what matters with a parallel build.
|
||||
lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
|
||||
while true; do
|
||||
if mkdir "$lockdir" >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
# FIXME: race condition here if user kills between mkdir and trap.
|
||||
trap "rmdir '$lockdir'; exit 1" 1 2 15
|
||||
|
||||
# Run the compile.
|
||||
"$@"
|
||||
ret=$?
|
||||
|
||||
if test -f "$cofile"; then
|
||||
test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
|
||||
elif test -f "${cofile}bj"; then
|
||||
test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
|
||||
fi
|
||||
|
||||
rmdir "$lockdir"
|
||||
exit $ret
|
||||
|
||||
# Local Variables:
|
||||
# mode: shell-script
|
||||
# sh-indentation: 2
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
||||
-708
@@ -1,708 +0,0 @@
|
||||
#! /bin/sh
|
||||
# depcomp - compile a program generating dependencies as side-effects
|
||||
|
||||
scriptversion=2012-03-27.16; # UTC
|
||||
|
||||
# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006, 2007, 2009, 2010,
|
||||
# 2011, 2012 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
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
|
||||
|
||||
case $1 in
|
||||
'')
|
||||
echo "$0: No command. Try '$0 --help' for more information." 1>&2
|
||||
exit 1;
|
||||
;;
|
||||
-h | --h*)
|
||||
cat <<\EOF
|
||||
Usage: depcomp [--help] [--version] PROGRAM [ARGS]
|
||||
|
||||
Run PROGRAMS ARGS to compile a file, generating dependencies
|
||||
as side-effects.
|
||||
|
||||
Environment variables:
|
||||
depmode Dependency tracking mode.
|
||||
source Source file read by 'PROGRAMS ARGS'.
|
||||
object Object file output by 'PROGRAMS ARGS'.
|
||||
DEPDIR directory where to store dependencies.
|
||||
depfile Dependency file to output.
|
||||
tmpdepfile Temporary file to use when outputting dependencies.
|
||||
libtool Whether libtool is used (yes/no).
|
||||
|
||||
Report bugs to <bug-automake@gnu.org>.
|
||||
EOF
|
||||
exit $?
|
||||
;;
|
||||
-v | --v*)
|
||||
echo "depcomp $scriptversion"
|
||||
exit $?
|
||||
;;
|
||||
esac
|
||||
|
||||
# A tabulation character.
|
||||
tab=' '
|
||||
# A newline character.
|
||||
nl='
|
||||
'
|
||||
|
||||
if test -z "$depmode" || test -z "$source" || test -z "$object"; then
|
||||
echo "depcomp: Variables source, object and depmode must be set" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
|
||||
depfile=${depfile-`echo "$object" |
|
||||
sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
|
||||
tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
|
||||
|
||||
rm -f "$tmpdepfile"
|
||||
|
||||
# Some modes work just like other modes, but use different flags. We
|
||||
# parameterize here, but still list the modes in the big case below,
|
||||
# to make depend.m4 easier to write. Note that we *cannot* use a case
|
||||
# here, because this file can only contain one case statement.
|
||||
if test "$depmode" = hp; then
|
||||
# HP compiler uses -M and no extra arg.
|
||||
gccflag=-M
|
||||
depmode=gcc
|
||||
fi
|
||||
|
||||
if test "$depmode" = dashXmstdout; then
|
||||
# This is just like dashmstdout with a different argument.
|
||||
dashmflag=-xM
|
||||
depmode=dashmstdout
|
||||
fi
|
||||
|
||||
cygpath_u="cygpath -u -f -"
|
||||
if test "$depmode" = msvcmsys; then
|
||||
# This is just like msvisualcpp but w/o cygpath translation.
|
||||
# Just convert the backslash-escaped backslashes to single forward
|
||||
# slashes to satisfy depend.m4
|
||||
cygpath_u='sed s,\\\\,/,g'
|
||||
depmode=msvisualcpp
|
||||
fi
|
||||
|
||||
if test "$depmode" = msvc7msys; then
|
||||
# This is just like msvc7 but w/o cygpath translation.
|
||||
# Just convert the backslash-escaped backslashes to single forward
|
||||
# slashes to satisfy depend.m4
|
||||
cygpath_u='sed s,\\\\,/,g'
|
||||
depmode=msvc7
|
||||
fi
|
||||
|
||||
if test "$depmode" = xlc; then
|
||||
# IBM C/C++ Compilers xlc/xlC can output gcc-like dependency informations.
|
||||
gccflag=-qmakedep=gcc,-MF
|
||||
depmode=gcc
|
||||
fi
|
||||
|
||||
case "$depmode" in
|
||||
gcc3)
|
||||
## gcc 3 implements dependency tracking that does exactly what
|
||||
## we want. Yay! Note: for some reason libtool 1.4 doesn't like
|
||||
## it if -MD -MP comes after the -MF stuff. Hmm.
|
||||
## Unfortunately, FreeBSD c89 acceptance of flags depends upon
|
||||
## the command line argument order; so add the flags where they
|
||||
## appear in depend2.am. Note that the slowdown incurred here
|
||||
## affects only configure: in makefiles, %FASTDEP% shortcuts this.
|
||||
for arg
|
||||
do
|
||||
case $arg in
|
||||
-c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
|
||||
*) set fnord "$@" "$arg" ;;
|
||||
esac
|
||||
shift # fnord
|
||||
shift # $arg
|
||||
done
|
||||
"$@"
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
mv "$tmpdepfile" "$depfile"
|
||||
;;
|
||||
|
||||
gcc)
|
||||
## There are various ways to get dependency output from gcc. Here's
|
||||
## why we pick this rather obscure method:
|
||||
## - Don't want to use -MD because we'd like the dependencies to end
|
||||
## up in a subdir. Having to rename by hand is ugly.
|
||||
## (We might end up doing this anyway to support other compilers.)
|
||||
## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
|
||||
## -MM, not -M (despite what the docs say).
|
||||
## - Using -M directly means running the compiler twice (even worse
|
||||
## than renaming).
|
||||
if test -z "$gccflag"; then
|
||||
gccflag=-MD,
|
||||
fi
|
||||
"$@" -Wp,"$gccflag$tmpdepfile"
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
|
||||
## The second -e expression handles DOS-style file names with drive letters.
|
||||
sed -e 's/^[^:]*: / /' \
|
||||
-e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
|
||||
## This next piece of magic avoids the "deleted header file" problem.
|
||||
## The problem is that when a header file which appears in a .P file
|
||||
## is deleted, the dependency causes make to die (because there is
|
||||
## typically no way to rebuild the header). We avoid this by adding
|
||||
## dummy dependencies for each header file. Too bad gcc doesn't do
|
||||
## this for us directly.
|
||||
tr ' ' "$nl" < "$tmpdepfile" |
|
||||
## Some versions of gcc put a space before the ':'. On the theory
|
||||
## that the space means something, we add a space to the output as
|
||||
## well. hp depmode also adds that space, but also prefixes the VPATH
|
||||
## to the object. Take care to not repeat it in the output.
|
||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
## correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
|
||||
| sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
hp)
|
||||
# This case exists only to let depend.m4 do its work. It works by
|
||||
# looking at the text of this script. This case will never be run,
|
||||
# since it is checked for above.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
sgi)
|
||||
if test "$libtool" = yes; then
|
||||
"$@" "-Wp,-MDupdate,$tmpdepfile"
|
||||
else
|
||||
"$@" -MDupdate "$tmpdepfile"
|
||||
fi
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
|
||||
if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
|
||||
echo "$object : \\" > "$depfile"
|
||||
|
||||
# Clip off the initial element (the dependent). Don't try to be
|
||||
# clever and replace this with sed code, as IRIX sed won't handle
|
||||
# lines with more than a fixed number of characters (4096 in
|
||||
# IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
|
||||
# the IRIX cc adds comments like '#:fec' to the end of the
|
||||
# dependency line.
|
||||
tr ' ' "$nl" < "$tmpdepfile" \
|
||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
|
||||
tr "$nl" ' ' >> "$depfile"
|
||||
echo >> "$depfile"
|
||||
|
||||
# The second pass generates a dummy entry for each header file.
|
||||
tr ' ' "$nl" < "$tmpdepfile" \
|
||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
|
||||
>> "$depfile"
|
||||
else
|
||||
# The sourcefile does not contain any dependencies, so just
|
||||
# store a dummy comment line, to avoid errors with the Makefile
|
||||
# "include basename.Plo" scheme.
|
||||
echo "#dummy" > "$depfile"
|
||||
fi
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
xlc)
|
||||
# This case exists only to let depend.m4 do its work. It works by
|
||||
# looking at the text of this script. This case will never be run,
|
||||
# since it is checked for above.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
aix)
|
||||
# The C for AIX Compiler uses -M and outputs the dependencies
|
||||
# in a .u file. In older versions, this file always lives in the
|
||||
# current directory. Also, the AIX compiler puts '$object:' at the
|
||||
# start of each line; $object doesn't have directory information.
|
||||
# Version 6 uses the directory in both cases.
|
||||
dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
|
||||
test "x$dir" = "x$object" && dir=
|
||||
base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
|
||||
if test "$libtool" = yes; then
|
||||
tmpdepfile1=$dir$base.u
|
||||
tmpdepfile2=$base.u
|
||||
tmpdepfile3=$dir.libs/$base.u
|
||||
"$@" -Wc,-M
|
||||
else
|
||||
tmpdepfile1=$dir$base.u
|
||||
tmpdepfile2=$dir$base.u
|
||||
tmpdepfile3=$dir$base.u
|
||||
"$@" -M
|
||||
fi
|
||||
stat=$?
|
||||
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||
exit $stat
|
||||
fi
|
||||
|
||||
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||
do
|
||||
test -f "$tmpdepfile" && break
|
||||
done
|
||||
if test -f "$tmpdepfile"; then
|
||||
# Each line is of the form 'foo.o: dependent.h'.
|
||||
# Do two passes, one to just change these to
|
||||
# '$object: dependent.h' and one to simply 'dependent.h:'.
|
||||
sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
|
||||
sed -e 's,^.*\.[a-z]*:['"$tab"' ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
|
||||
else
|
||||
# The sourcefile does not contain any dependencies, so just
|
||||
# store a dummy comment line, to avoid errors with the Makefile
|
||||
# "include basename.Plo" scheme.
|
||||
echo "#dummy" > "$depfile"
|
||||
fi
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
icc)
|
||||
# Intel's C compiler anf tcc (Tiny C Compiler) understand '-MD -MF file'.
|
||||
# However on
|
||||
# $CC -MD -MF foo.d -c -o sub/foo.o sub/foo.c
|
||||
# ICC 7.0 will fill foo.d with something like
|
||||
# foo.o: sub/foo.c
|
||||
# foo.o: sub/foo.h
|
||||
# which is wrong. We want
|
||||
# sub/foo.o: sub/foo.c
|
||||
# sub/foo.o: sub/foo.h
|
||||
# sub/foo.c:
|
||||
# sub/foo.h:
|
||||
# ICC 7.1 will output
|
||||
# foo.o: sub/foo.c sub/foo.h
|
||||
# and will wrap long lines using '\':
|
||||
# foo.o: sub/foo.c ... \
|
||||
# sub/foo.h ... \
|
||||
# ...
|
||||
# tcc 0.9.26 (FIXME still under development at the moment of writing)
|
||||
# will emit a similar output, but also prepend the continuation lines
|
||||
# with horizontal tabulation characters.
|
||||
"$@" -MD -MF "$tmpdepfile"
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
# Each line is of the form 'foo.o: dependent.h',
|
||||
# or 'foo.o: dep1.h dep2.h \', or ' dep3.h dep4.h \'.
|
||||
# Do two passes, one to just change these to
|
||||
# '$object: dependent.h' and one to simply 'dependent.h:'.
|
||||
sed -e "s/^[ $tab][ $tab]*/ /" -e "s,^[^:]*:,$object :," \
|
||||
< "$tmpdepfile" > "$depfile"
|
||||
sed '
|
||||
s/[ '"$tab"'][ '"$tab"']*/ /g
|
||||
s/^ *//
|
||||
s/ *\\*$//
|
||||
s/^[^:]*: *//
|
||||
/^$/d
|
||||
/:$/d
|
||||
s/$/ :/
|
||||
' < "$tmpdepfile" >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
hp2)
|
||||
# The "hp" stanza above does not work with aCC (C++) and HP's ia64
|
||||
# compilers, which have integrated preprocessors. The correct option
|
||||
# to use with these is +Maked; it writes dependencies to a file named
|
||||
# 'foo.d', which lands next to the object file, wherever that
|
||||
# happens to be.
|
||||
# Much of this is similar to the tru64 case; see comments there.
|
||||
dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
|
||||
test "x$dir" = "x$object" && dir=
|
||||
base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
|
||||
if test "$libtool" = yes; then
|
||||
tmpdepfile1=$dir$base.d
|
||||
tmpdepfile2=$dir.libs/$base.d
|
||||
"$@" -Wc,+Maked
|
||||
else
|
||||
tmpdepfile1=$dir$base.d
|
||||
tmpdepfile2=$dir$base.d
|
||||
"$@" +Maked
|
||||
fi
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile1" "$tmpdepfile2"
|
||||
exit $stat
|
||||
fi
|
||||
|
||||
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
|
||||
do
|
||||
test -f "$tmpdepfile" && break
|
||||
done
|
||||
if test -f "$tmpdepfile"; then
|
||||
sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile"
|
||||
# Add 'dependent.h:' lines.
|
||||
sed -ne '2,${
|
||||
s/^ *//
|
||||
s/ \\*$//
|
||||
s/$/:/
|
||||
p
|
||||
}' "$tmpdepfile" >> "$depfile"
|
||||
else
|
||||
echo "#dummy" > "$depfile"
|
||||
fi
|
||||
rm -f "$tmpdepfile" "$tmpdepfile2"
|
||||
;;
|
||||
|
||||
tru64)
|
||||
# The Tru64 compiler uses -MD to generate dependencies as a side
|
||||
# effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'.
|
||||
# At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
|
||||
# dependencies in 'foo.d' instead, so we check for that too.
|
||||
# Subdirectories are respected.
|
||||
dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
|
||||
test "x$dir" = "x$object" && dir=
|
||||
base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
|
||||
|
||||
if test "$libtool" = yes; then
|
||||
# With Tru64 cc, shared objects can also be used to make a
|
||||
# static library. This mechanism is used in libtool 1.4 series to
|
||||
# handle both shared and static libraries in a single compilation.
|
||||
# With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d.
|
||||
#
|
||||
# With libtool 1.5 this exception was removed, and libtool now
|
||||
# generates 2 separate objects for the 2 libraries. These two
|
||||
# compilations output dependencies in $dir.libs/$base.o.d and
|
||||
# in $dir$base.o.d. We have to check for both files, because
|
||||
# one of the two compilations can be disabled. We should prefer
|
||||
# $dir$base.o.d over $dir.libs/$base.o.d because the latter is
|
||||
# automatically cleaned when .libs/ is deleted, while ignoring
|
||||
# the former would cause a distcleancheck panic.
|
||||
tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4
|
||||
tmpdepfile2=$dir$base.o.d # libtool 1.5
|
||||
tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5
|
||||
tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504
|
||||
"$@" -Wc,-MD
|
||||
else
|
||||
tmpdepfile1=$dir$base.o.d
|
||||
tmpdepfile2=$dir$base.d
|
||||
tmpdepfile3=$dir$base.d
|
||||
tmpdepfile4=$dir$base.d
|
||||
"$@" -MD
|
||||
fi
|
||||
|
||||
stat=$?
|
||||
if test $stat -eq 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
|
||||
exit $stat
|
||||
fi
|
||||
|
||||
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
|
||||
do
|
||||
test -f "$tmpdepfile" && break
|
||||
done
|
||||
if test -f "$tmpdepfile"; then
|
||||
sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
|
||||
sed -e 's,^.*\.[a-z]*:['"$tab"' ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
|
||||
else
|
||||
echo "#dummy" > "$depfile"
|
||||
fi
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
msvc7)
|
||||
if test "$libtool" = yes; then
|
||||
showIncludes=-Wc,-showIncludes
|
||||
else
|
||||
showIncludes=-showIncludes
|
||||
fi
|
||||
"$@" $showIncludes > "$tmpdepfile"
|
||||
stat=$?
|
||||
grep -v '^Note: including file: ' "$tmpdepfile"
|
||||
if test "$stat" = 0; then :
|
||||
else
|
||||
rm -f "$tmpdepfile"
|
||||
exit $stat
|
||||
fi
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
# The first sed program below extracts the file names and escapes
|
||||
# backslashes for cygpath. The second sed program outputs the file
|
||||
# name when reading, but also accumulates all include files in the
|
||||
# hold buffer in order to output them again at the end. This only
|
||||
# works with sed implementations that can handle large buffers.
|
||||
sed < "$tmpdepfile" -n '
|
||||
/^Note: including file: *\(.*\)/ {
|
||||
s//\1/
|
||||
s/\\/\\\\/g
|
||||
p
|
||||
}' | $cygpath_u | sort -u | sed -n '
|
||||
s/ /\\ /g
|
||||
s/\(.*\)/'"$tab"'\1 \\/p
|
||||
s/.\(.*\) \\/\1:/
|
||||
H
|
||||
$ {
|
||||
s/.*/'"$tab"'/
|
||||
G
|
||||
p
|
||||
}' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
msvc7msys)
|
||||
# This case exists only to let depend.m4 do its work. It works by
|
||||
# looking at the text of this script. This case will never be run,
|
||||
# since it is checked for above.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
#nosideeffect)
|
||||
# This comment above is used by automake to tell side-effect
|
||||
# dependency tracking mechanisms from slower ones.
|
||||
|
||||
dashmstdout)
|
||||
# Important note: in order to support this mode, a compiler *must*
|
||||
# always write the preprocessed file to stdout, regardless of -o.
|
||||
"$@" || exit $?
|
||||
|
||||
# Remove the call to Libtool.
|
||||
if test "$libtool" = yes; then
|
||||
while test "X$1" != 'X--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
|
||||
# Remove '-o $object'.
|
||||
IFS=" "
|
||||
for arg
|
||||
do
|
||||
case $arg in
|
||||
-o)
|
||||
shift
|
||||
;;
|
||||
$object)
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"
|
||||
shift # fnord
|
||||
shift # $arg
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
test -z "$dashmflag" && dashmflag=-M
|
||||
# Require at least two characters before searching for ':'
|
||||
# in the target name. This is to cope with DOS-style filenames:
|
||||
# a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
|
||||
"$@" $dashmflag |
|
||||
sed 's:^['"$tab"' ]*[^:'"$tab"' ][^:][^:]*\:['"$tab"' ]*:'"$object"'\: :' > "$tmpdepfile"
|
||||
rm -f "$depfile"
|
||||
cat < "$tmpdepfile" > "$depfile"
|
||||
tr ' ' "$nl" < "$tmpdepfile" | \
|
||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
## correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
dashXmstdout)
|
||||
# This case only exists to satisfy depend.m4. It is never actually
|
||||
# run, as this mode is specially recognized in the preamble.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
makedepend)
|
||||
"$@" || exit $?
|
||||
# Remove any Libtool call
|
||||
if test "$libtool" = yes; then
|
||||
while test "X$1" != 'X--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
# X makedepend
|
||||
shift
|
||||
cleared=no eat=no
|
||||
for arg
|
||||
do
|
||||
case $cleared in
|
||||
no)
|
||||
set ""; shift
|
||||
cleared=yes ;;
|
||||
esac
|
||||
if test $eat = yes; then
|
||||
eat=no
|
||||
continue
|
||||
fi
|
||||
case "$arg" in
|
||||
-D*|-I*)
|
||||
set fnord "$@" "$arg"; shift ;;
|
||||
# Strip any option that makedepend may not understand. Remove
|
||||
# the object too, otherwise makedepend will parse it as a source file.
|
||||
-arch)
|
||||
eat=yes ;;
|
||||
-*|$object)
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"; shift ;;
|
||||
esac
|
||||
done
|
||||
obj_suffix=`echo "$object" | sed 's/^.*\././'`
|
||||
touch "$tmpdepfile"
|
||||
${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
|
||||
rm -f "$depfile"
|
||||
# makedepend may prepend the VPATH from the source file name to the object.
|
||||
# No need to regex-escape $object, excess matching of '.' is harmless.
|
||||
sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
|
||||
sed '1,2d' "$tmpdepfile" | tr ' ' "$nl" | \
|
||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||
## correctly. Breaking it into two sed invocations is a workaround.
|
||||
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile" "$tmpdepfile".bak
|
||||
;;
|
||||
|
||||
cpp)
|
||||
# Important note: in order to support this mode, a compiler *must*
|
||||
# always write the preprocessed file to stdout.
|
||||
"$@" || exit $?
|
||||
|
||||
# Remove the call to Libtool.
|
||||
if test "$libtool" = yes; then
|
||||
while test "X$1" != 'X--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
|
||||
# Remove '-o $object'.
|
||||
IFS=" "
|
||||
for arg
|
||||
do
|
||||
case $arg in
|
||||
-o)
|
||||
shift
|
||||
;;
|
||||
$object)
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"
|
||||
shift # fnord
|
||||
shift # $arg
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
"$@" -E |
|
||||
sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
|
||||
-e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
|
||||
sed '$ s: \\$::' > "$tmpdepfile"
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
cat < "$tmpdepfile" >> "$depfile"
|
||||
sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
msvisualcpp)
|
||||
# Important note: in order to support this mode, a compiler *must*
|
||||
# always write the preprocessed file to stdout.
|
||||
"$@" || exit $?
|
||||
|
||||
# Remove the call to Libtool.
|
||||
if test "$libtool" = yes; then
|
||||
while test "X$1" != 'X--mode=compile'; do
|
||||
shift
|
||||
done
|
||||
shift
|
||||
fi
|
||||
|
||||
IFS=" "
|
||||
for arg
|
||||
do
|
||||
case "$arg" in
|
||||
-o)
|
||||
shift
|
||||
;;
|
||||
$object)
|
||||
shift
|
||||
;;
|
||||
"-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
|
||||
set fnord "$@"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set fnord "$@" "$arg"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
"$@" -E 2>/dev/null |
|
||||
sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
|
||||
rm -f "$depfile"
|
||||
echo "$object : \\" > "$depfile"
|
||||
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile"
|
||||
echo "$tab" >> "$depfile"
|
||||
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
|
||||
rm -f "$tmpdepfile"
|
||||
;;
|
||||
|
||||
msvcmsys)
|
||||
# This case exists only to let depend.m4 do its work. It works by
|
||||
# looking at the text of this script. This case will never be run,
|
||||
# since it is checked for above.
|
||||
exit 1
|
||||
;;
|
||||
|
||||
none)
|
||||
exec "$@"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Unknown depmode $depmode" 1>&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
||||
# Local Variables:
|
||||
# mode: shell-script
|
||||
# sh-indentation: 2
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
||||
@@ -1,527 +0,0 @@
|
||||
#!/bin/sh
|
||||
# install - install a program, script, or datafile
|
||||
|
||||
scriptversion=2011-01-19.21; # UTC
|
||||
|
||||
# This originates from X11R5 (mit/util/scripts/install.sh), which was
|
||||
# later released in X11R6 (xc/config/util/install.sh) with the
|
||||
# following copyright and license.
|
||||
#
|
||||
# Copyright (C) 1994 X Consortium
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to
|
||||
# deal in the Software without restriction, including without limitation the
|
||||
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
# sell copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
|
||||
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
# Except as contained in this notice, the name of the X Consortium shall not
|
||||
# be used in advertising or otherwise to promote the sale, use or other deal-
|
||||
# ings in this Software without prior written authorization from the X Consor-
|
||||
# tium.
|
||||
#
|
||||
#
|
||||
# FSF changes to this file are in the public domain.
|
||||
#
|
||||
# Calling this script install-sh is preferred over install.sh, to prevent
|
||||
# `make' implicit rules from creating a file called install from it
|
||||
# when there is no Makefile.
|
||||
#
|
||||
# This script is compatible with the BSD install script, but was written
|
||||
# from scratch.
|
||||
|
||||
nl='
|
||||
'
|
||||
IFS=" "" $nl"
|
||||
|
||||
# set DOITPROG to echo to test this script
|
||||
|
||||
# Don't use :- since 4.3BSD and earlier shells don't like it.
|
||||
doit=${DOITPROG-}
|
||||
if test -z "$doit"; then
|
||||
doit_exec=exec
|
||||
else
|
||||
doit_exec=$doit
|
||||
fi
|
||||
|
||||
# Put in absolute file names if you don't have them in your path;
|
||||
# or use environment vars.
|
||||
|
||||
chgrpprog=${CHGRPPROG-chgrp}
|
||||
chmodprog=${CHMODPROG-chmod}
|
||||
chownprog=${CHOWNPROG-chown}
|
||||
cmpprog=${CMPPROG-cmp}
|
||||
cpprog=${CPPROG-cp}
|
||||
mkdirprog=${MKDIRPROG-mkdir}
|
||||
mvprog=${MVPROG-mv}
|
||||
rmprog=${RMPROG-rm}
|
||||
stripprog=${STRIPPROG-strip}
|
||||
|
||||
posix_glob='?'
|
||||
initialize_posix_glob='
|
||||
test "$posix_glob" != "?" || {
|
||||
if (set -f) 2>/dev/null; then
|
||||
posix_glob=
|
||||
else
|
||||
posix_glob=:
|
||||
fi
|
||||
}
|
||||
'
|
||||
|
||||
posix_mkdir=
|
||||
|
||||
# Desired mode of installed file.
|
||||
mode=0755
|
||||
|
||||
chgrpcmd=
|
||||
chmodcmd=$chmodprog
|
||||
chowncmd=
|
||||
mvcmd=$mvprog
|
||||
rmcmd="$rmprog -f"
|
||||
stripcmd=
|
||||
|
||||
src=
|
||||
dst=
|
||||
dir_arg=
|
||||
dst_arg=
|
||||
|
||||
copy_on_change=false
|
||||
no_target_directory=
|
||||
|
||||
usage="\
|
||||
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
|
||||
or: $0 [OPTION]... SRCFILES... DIRECTORY
|
||||
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
|
||||
or: $0 [OPTION]... -d DIRECTORIES...
|
||||
|
||||
In the 1st form, copy SRCFILE to DSTFILE.
|
||||
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
|
||||
In the 4th, create DIRECTORIES.
|
||||
|
||||
Options:
|
||||
--help display this help and exit.
|
||||
--version display version info and exit.
|
||||
|
||||
-c (ignored)
|
||||
-C install only if different (preserve the last data modification time)
|
||||
-d create directories instead of installing files.
|
||||
-g GROUP $chgrpprog installed files to GROUP.
|
||||
-m MODE $chmodprog installed files to MODE.
|
||||
-o USER $chownprog installed files to USER.
|
||||
-s $stripprog installed files.
|
||||
-t DIRECTORY install into DIRECTORY.
|
||||
-T report an error if DSTFILE is a directory.
|
||||
|
||||
Environment variables override the default commands:
|
||||
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
|
||||
RMPROG STRIPPROG
|
||||
"
|
||||
|
||||
while test $# -ne 0; do
|
||||
case $1 in
|
||||
-c) ;;
|
||||
|
||||
-C) copy_on_change=true;;
|
||||
|
||||
-d) dir_arg=true;;
|
||||
|
||||
-g) chgrpcmd="$chgrpprog $2"
|
||||
shift;;
|
||||
|
||||
--help) echo "$usage"; exit $?;;
|
||||
|
||||
-m) mode=$2
|
||||
case $mode in
|
||||
*' '* | *' '* | *'
|
||||
'* | *'*'* | *'?'* | *'['*)
|
||||
echo "$0: invalid mode: $mode" >&2
|
||||
exit 1;;
|
||||
esac
|
||||
shift;;
|
||||
|
||||
-o) chowncmd="$chownprog $2"
|
||||
shift;;
|
||||
|
||||
-s) stripcmd=$stripprog;;
|
||||
|
||||
-t) dst_arg=$2
|
||||
# Protect names problematic for `test' and other utilities.
|
||||
case $dst_arg in
|
||||
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||
esac
|
||||
shift;;
|
||||
|
||||
-T) no_target_directory=true;;
|
||||
|
||||
--version) echo "$0 $scriptversion"; exit $?;;
|
||||
|
||||
--) shift
|
||||
break;;
|
||||
|
||||
-*) echo "$0: invalid option: $1" >&2
|
||||
exit 1;;
|
||||
|
||||
*) break;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
|
||||
# When -d is used, all remaining arguments are directories to create.
|
||||
# When -t is used, the destination is already specified.
|
||||
# Otherwise, the last argument is the destination. Remove it from $@.
|
||||
for arg
|
||||
do
|
||||
if test -n "$dst_arg"; then
|
||||
# $@ is not empty: it contains at least $arg.
|
||||
set fnord "$@" "$dst_arg"
|
||||
shift # fnord
|
||||
fi
|
||||
shift # arg
|
||||
dst_arg=$arg
|
||||
# Protect names problematic for `test' and other utilities.
|
||||
case $dst_arg in
|
||||
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
if test $# -eq 0; then
|
||||
if test -z "$dir_arg"; then
|
||||
echo "$0: no input file specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
# It's OK to call `install-sh -d' without argument.
|
||||
# This can happen when creating conditional directories.
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if test -z "$dir_arg"; then
|
||||
do_exit='(exit $ret); exit $ret'
|
||||
trap "ret=129; $do_exit" 1
|
||||
trap "ret=130; $do_exit" 2
|
||||
trap "ret=141; $do_exit" 13
|
||||
trap "ret=143; $do_exit" 15
|
||||
|
||||
# Set umask so as not to create temps with too-generous modes.
|
||||
# However, 'strip' requires both read and write access to temps.
|
||||
case $mode in
|
||||
# Optimize common cases.
|
||||
*644) cp_umask=133;;
|
||||
*755) cp_umask=22;;
|
||||
|
||||
*[0-7])
|
||||
if test -z "$stripcmd"; then
|
||||
u_plus_rw=
|
||||
else
|
||||
u_plus_rw='% 200'
|
||||
fi
|
||||
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
|
||||
*)
|
||||
if test -z "$stripcmd"; then
|
||||
u_plus_rw=
|
||||
else
|
||||
u_plus_rw=,u+rw
|
||||
fi
|
||||
cp_umask=$mode$u_plus_rw;;
|
||||
esac
|
||||
fi
|
||||
|
||||
for src
|
||||
do
|
||||
# Protect names problematic for `test' and other utilities.
|
||||
case $src in
|
||||
-* | [=\(\)!]) src=./$src;;
|
||||
esac
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
dst=$src
|
||||
dstdir=$dst
|
||||
test -d "$dstdir"
|
||||
dstdir_status=$?
|
||||
else
|
||||
|
||||
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
|
||||
# might cause directories to be created, which would be especially bad
|
||||
# if $src (and thus $dsttmp) contains '*'.
|
||||
if test ! -f "$src" && test ! -d "$src"; then
|
||||
echo "$0: $src does not exist." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test -z "$dst_arg"; then
|
||||
echo "$0: no destination specified." >&2
|
||||
exit 1
|
||||
fi
|
||||
dst=$dst_arg
|
||||
|
||||
# If destination is a directory, append the input filename; won't work
|
||||
# if double slashes aren't ignored.
|
||||
if test -d "$dst"; then
|
||||
if test -n "$no_target_directory"; then
|
||||
echo "$0: $dst_arg: Is a directory" >&2
|
||||
exit 1
|
||||
fi
|
||||
dstdir=$dst
|
||||
dst=$dstdir/`basename "$src"`
|
||||
dstdir_status=0
|
||||
else
|
||||
# Prefer dirname, but fall back on a substitute if dirname fails.
|
||||
dstdir=`
|
||||
(dirname "$dst") 2>/dev/null ||
|
||||
expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
|
||||
X"$dst" : 'X\(//\)[^/]' \| \
|
||||
X"$dst" : 'X\(//\)$' \| \
|
||||
X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
|
||||
echo X"$dst" |
|
||||
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
/^X\(\/\/\)[^/].*/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
/^X\(\/\/\)$/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
/^X\(\/\).*/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
s/.*/./; q'
|
||||
`
|
||||
|
||||
test -d "$dstdir"
|
||||
dstdir_status=$?
|
||||
fi
|
||||
fi
|
||||
|
||||
obsolete_mkdir_used=false
|
||||
|
||||
if test $dstdir_status != 0; then
|
||||
case $posix_mkdir in
|
||||
'')
|
||||
# Create intermediate dirs using mode 755 as modified by the umask.
|
||||
# This is like FreeBSD 'install' as of 1997-10-28.
|
||||
umask=`umask`
|
||||
case $stripcmd.$umask in
|
||||
# Optimize common cases.
|
||||
*[2367][2367]) mkdir_umask=$umask;;
|
||||
.*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
|
||||
|
||||
*[0-7])
|
||||
mkdir_umask=`expr $umask + 22 \
|
||||
- $umask % 100 % 40 + $umask % 20 \
|
||||
- $umask % 10 % 4 + $umask % 2
|
||||
`;;
|
||||
*) mkdir_umask=$umask,go-w;;
|
||||
esac
|
||||
|
||||
# With -d, create the new directory with the user-specified mode.
|
||||
# Otherwise, rely on $mkdir_umask.
|
||||
if test -n "$dir_arg"; then
|
||||
mkdir_mode=-m$mode
|
||||
else
|
||||
mkdir_mode=
|
||||
fi
|
||||
|
||||
posix_mkdir=false
|
||||
case $umask in
|
||||
*[123567][0-7][0-7])
|
||||
# POSIX mkdir -p sets u+wx bits regardless of umask, which
|
||||
# is incompatible with FreeBSD 'install' when (umask & 300) != 0.
|
||||
;;
|
||||
*)
|
||||
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
|
||||
trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
|
||||
|
||||
if (umask $mkdir_umask &&
|
||||
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
|
||||
then
|
||||
if test -z "$dir_arg" || {
|
||||
# Check for POSIX incompatibilities with -m.
|
||||
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
|
||||
# other-writeable bit of parent directory when it shouldn't.
|
||||
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
|
||||
ls_ld_tmpdir=`ls -ld "$tmpdir"`
|
||||
case $ls_ld_tmpdir in
|
||||
d????-?r-*) different_mode=700;;
|
||||
d????-?--*) different_mode=755;;
|
||||
*) false;;
|
||||
esac &&
|
||||
$mkdirprog -m$different_mode -p -- "$tmpdir" && {
|
||||
ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
|
||||
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
|
||||
}
|
||||
}
|
||||
then posix_mkdir=:
|
||||
fi
|
||||
rmdir "$tmpdir/d" "$tmpdir"
|
||||
else
|
||||
# Remove any dirs left behind by ancient mkdir implementations.
|
||||
rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
|
||||
fi
|
||||
trap '' 0;;
|
||||
esac;;
|
||||
esac
|
||||
|
||||
if
|
||||
$posix_mkdir && (
|
||||
umask $mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
|
||||
)
|
||||
then :
|
||||
else
|
||||
|
||||
# The umask is ridiculous, or mkdir does not conform to POSIX,
|
||||
# or it failed possibly due to a race condition. Create the
|
||||
# directory the slow way, step by step, checking for races as we go.
|
||||
|
||||
case $dstdir in
|
||||
/*) prefix='/';;
|
||||
[-=\(\)!]*) prefix='./';;
|
||||
*) prefix='';;
|
||||
esac
|
||||
|
||||
eval "$initialize_posix_glob"
|
||||
|
||||
oIFS=$IFS
|
||||
IFS=/
|
||||
$posix_glob set -f
|
||||
set fnord $dstdir
|
||||
shift
|
||||
$posix_glob set +f
|
||||
IFS=$oIFS
|
||||
|
||||
prefixes=
|
||||
|
||||
for d
|
||||
do
|
||||
test X"$d" = X && continue
|
||||
|
||||
prefix=$prefix$d
|
||||
if test -d "$prefix"; then
|
||||
prefixes=
|
||||
else
|
||||
if $posix_mkdir; then
|
||||
(umask=$mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
|
||||
# Don't fail if two instances are running concurrently.
|
||||
test -d "$prefix" || exit 1
|
||||
else
|
||||
case $prefix in
|
||||
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
|
||||
*) qprefix=$prefix;;
|
||||
esac
|
||||
prefixes="$prefixes '$qprefix'"
|
||||
fi
|
||||
fi
|
||||
prefix=$prefix/
|
||||
done
|
||||
|
||||
if test -n "$prefixes"; then
|
||||
# Don't fail if two instances are running concurrently.
|
||||
(umask $mkdir_umask &&
|
||||
eval "\$doit_exec \$mkdirprog $prefixes") ||
|
||||
test -d "$dstdir" || exit 1
|
||||
obsolete_mkdir_used=true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
|
||||
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
|
||||
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
|
||||
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
|
||||
else
|
||||
|
||||
# Make a couple of temp file names in the proper directory.
|
||||
dsttmp=$dstdir/_inst.$$_
|
||||
rmtmp=$dstdir/_rm.$$_
|
||||
|
||||
# Trap to clean up those temp files at exit.
|
||||
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
|
||||
|
||||
# Copy the file name to the temp name.
|
||||
(umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
|
||||
|
||||
# and set any options; do chmod last to preserve setuid bits.
|
||||
#
|
||||
# If any of these fail, we abort the whole thing. If we want to
|
||||
# ignore errors from any of these, just make sure not to ignore
|
||||
# errors from the above "$doit $cpprog $src $dsttmp" command.
|
||||
#
|
||||
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
|
||||
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
|
||||
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
|
||||
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
|
||||
|
||||
# If -C, don't bother to copy if it wouldn't change the file.
|
||||
if $copy_on_change &&
|
||||
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
|
||||
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
|
||||
|
||||
eval "$initialize_posix_glob" &&
|
||||
$posix_glob set -f &&
|
||||
set X $old && old=:$2:$4:$5:$6 &&
|
||||
set X $new && new=:$2:$4:$5:$6 &&
|
||||
$posix_glob set +f &&
|
||||
|
||||
test "$old" = "$new" &&
|
||||
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
|
||||
then
|
||||
rm -f "$dsttmp"
|
||||
else
|
||||
# Rename the file to the real destination.
|
||||
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
|
||||
|
||||
# The rename failed, perhaps because mv can't rename something else
|
||||
# to itself, or perhaps because mv is so ancient that it does not
|
||||
# support -f.
|
||||
{
|
||||
# Now remove or move aside any old file at destination location.
|
||||
# We try this two ways since rm can't unlink itself on some
|
||||
# systems and the destination file might be busy for other
|
||||
# reasons. In this case, the final cleanup might fail but the new
|
||||
# file should still install successfully.
|
||||
{
|
||||
test ! -f "$dst" ||
|
||||
$doit $rmcmd -f "$dst" 2>/dev/null ||
|
||||
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
|
||||
{ $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
|
||||
} ||
|
||||
{ echo "$0: cannot unlink or rename $dst" >&2
|
||||
(exit 1); exit 1
|
||||
}
|
||||
} &&
|
||||
|
||||
# Now rename the file to the real destination.
|
||||
$doit $mvcmd "$dsttmp" "$dst"
|
||||
}
|
||||
fi || exit 1
|
||||
|
||||
trap '' 0
|
||||
fi
|
||||
done
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
||||
@@ -0,0 +1,2 @@
|
||||
libtool.m4
|
||||
lt*.m4
|
||||
@@ -0,0 +1,4 @@
|
||||
dnl AC_FIND_FUNC(func, arguments)
|
||||
AC_DEFUN([AC_CHECK_MATH_FUNC], [
|
||||
AC_FIND_FUNC_NO_LIBS([$1], [m], [#include <math.h>], [$2])
|
||||
])
|
||||
@@ -0,0 +1,8 @@
|
||||
dnl Shamelessly stolen from Heimdal
|
||||
dnl
|
||||
dnl Look for function in any of the specified libraries
|
||||
dnl
|
||||
|
||||
dnl AC_FIND_FUNC_NO_LIBS(func, libraries, includes, arguments, extra libs, extra args)
|
||||
AC_DEFUN([AC_FIND_FUNC_NO_LIBS], [
|
||||
AC_FIND_FUNC_NO_LIBS2([$1], ["" $2], [$3], [$4], [$5], [$6])])
|
||||
@@ -0,0 +1,62 @@
|
||||
dnl Shamelessly stolen from Heimdal
|
||||
dnl
|
||||
dnl Look for function in any of the specified libraries
|
||||
dnl
|
||||
|
||||
dnl AC_FIND_FUNC_NO_LIBS2(func, libraries, includes, arguments, extra libs, extra args)
|
||||
AC_DEFUN([AC_FIND_FUNC_NO_LIBS2], [
|
||||
|
||||
AC_MSG_CHECKING([for $1])
|
||||
AC_CACHE_VAL(ac_cv_funclib_$1,
|
||||
[
|
||||
if eval "test \"\$ac_cv_func_$1\" != yes" ; then
|
||||
ac_save_LIBS="$LIBS"
|
||||
for ac_lib in $2; do
|
||||
case "$ac_lib" in
|
||||
"") ;;
|
||||
yes) ac_lib="" ;;
|
||||
no) continue ;;
|
||||
-l*) ;;
|
||||
*) ac_lib="-l$ac_lib" ;;
|
||||
esac
|
||||
LIBS="$6 $ac_lib $5 $ac_save_LIBS"
|
||||
AC_LINK_IFELSE([AC_LANG_PROGRAM([[$3]],[[$1($4)]])],[eval "if test -n \"$ac_lib\";then ac_cv_funclib_$1=$ac_lib; else ac_cv_funclib_$1=yes; fi";break])
|
||||
done
|
||||
eval "ac_cv_funclib_$1=\${ac_cv_funclib_$1-no}"
|
||||
LIBS="$ac_save_LIBS"
|
||||
fi
|
||||
])
|
||||
|
||||
eval "ac_res=\$ac_cv_funclib_$1"
|
||||
|
||||
if false; then
|
||||
AC_CHECK_FUNCS($1)
|
||||
dnl AC_CHECK_LIBS($2, foo)
|
||||
fi
|
||||
# $1
|
||||
eval "ac_tr_func=HAVE_[]upcase($1)"
|
||||
eval "ac_tr_lib=HAVE_LIB[]upcase($ac_res | sed -e 's/-l//')"
|
||||
eval "LIB_$1=$ac_res"
|
||||
|
||||
case "$ac_res" in
|
||||
yes)
|
||||
eval "ac_cv_func_$1=yes"
|
||||
eval "LIB_$1="
|
||||
AC_DEFINE_UNQUOTED($ac_tr_func)
|
||||
AC_MSG_RESULT([yes])
|
||||
;;
|
||||
no)
|
||||
eval "ac_cv_func_$1=no"
|
||||
eval "LIB_$1="
|
||||
AC_MSG_RESULT([no])
|
||||
;;
|
||||
*)
|
||||
eval "ac_cv_func_$1=yes"
|
||||
eval "ac_cv_lib_`echo "$ac_res" | sed 's/-l//'`=yes"
|
||||
AC_DEFINE_UNQUOTED($ac_tr_func)
|
||||
AC_DEFINE_UNQUOTED($ac_tr_lib)
|
||||
AC_MSG_RESULT([yes, in $ac_res])
|
||||
;;
|
||||
esac
|
||||
AC_SUBST(LIB_$1)
|
||||
])
|
||||
@@ -0,0 +1,9 @@
|
||||
dnl Shamelessly stolen from Heimdal
|
||||
dnl
|
||||
dnl AC_FIND_FUNC(func, libraries, includes, arguments)
|
||||
AC_DEFUN([AC_FIND_FUNC], [
|
||||
AC_FIND_FUNC_NO_LIBS([$1], [$2], [$3], [$4])
|
||||
if test -n "$LIB_$1"; then
|
||||
LIBS="$LIB_$1 $LIBS"
|
||||
fi
|
||||
])
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
dnl Shamelessly stolen from Heimdal
|
||||
AC_DEFUN([upcase],[`echo $1 | tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ`])dnl
|
||||
-331
@@ -1,331 +0,0 @@
|
||||
#! /bin/sh
|
||||
# Common stub for a few missing GNU programs while installing.
|
||||
|
||||
scriptversion=2012-01-06.13; # UTC
|
||||
|
||||
# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006,
|
||||
# 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
|
||||
# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
|
||||
|
||||
# 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
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
if test $# -eq 0; then
|
||||
echo 1>&2 "Try \`$0 --help' for more information"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
run=:
|
||||
sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p'
|
||||
sed_minuso='s/.* -o \([^ ]*\).*/\1/p'
|
||||
|
||||
# In the cases where this matters, `missing' is being run in the
|
||||
# srcdir already.
|
||||
if test -f configure.ac; then
|
||||
configure_ac=configure.ac
|
||||
else
|
||||
configure_ac=configure.in
|
||||
fi
|
||||
|
||||
msg="missing on your system"
|
||||
|
||||
case $1 in
|
||||
--run)
|
||||
# Try to run requested program, and just exit if it succeeds.
|
||||
run=
|
||||
shift
|
||||
"$@" && exit 0
|
||||
# Exit code 63 means version mismatch. This often happens
|
||||
# when the user try to use an ancient version of a tool on
|
||||
# a file that requires a minimum version. In this case we
|
||||
# we should proceed has if the program had been absent, or
|
||||
# if --run hadn't been passed.
|
||||
if test $? = 63; then
|
||||
run=:
|
||||
msg="probably too old"
|
||||
fi
|
||||
;;
|
||||
|
||||
-h|--h|--he|--hel|--help)
|
||||
echo "\
|
||||
$0 [OPTION]... PROGRAM [ARGUMENT]...
|
||||
|
||||
Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
|
||||
error status if there is no known handling for PROGRAM.
|
||||
|
||||
Options:
|
||||
-h, --help display this help and exit
|
||||
-v, --version output version information and exit
|
||||
--run try to run the given command, and emulate it if it fails
|
||||
|
||||
Supported PROGRAM values:
|
||||
aclocal touch file \`aclocal.m4'
|
||||
autoconf touch file \`configure'
|
||||
autoheader touch file \`config.h.in'
|
||||
autom4te touch the output file, or create a stub one
|
||||
automake touch all \`Makefile.in' files
|
||||
bison create \`y.tab.[ch]', if possible, from existing .[ch]
|
||||
flex create \`lex.yy.c', if possible, from existing .c
|
||||
help2man touch the output file
|
||||
lex create \`lex.yy.c', if possible, from existing .c
|
||||
makeinfo touch the output file
|
||||
yacc create \`y.tab.[ch]', if possible, from existing .[ch]
|
||||
|
||||
Version suffixes to PROGRAM as well as the prefixes \`gnu-', \`gnu', and
|
||||
\`g' are ignored when checking the name.
|
||||
|
||||
Send bug reports to <bug-automake@gnu.org>."
|
||||
exit $?
|
||||
;;
|
||||
|
||||
-v|--v|--ve|--ver|--vers|--versi|--versio|--version)
|
||||
echo "missing $scriptversion (GNU Automake)"
|
||||
exit $?
|
||||
;;
|
||||
|
||||
-*)
|
||||
echo 1>&2 "$0: Unknown \`$1' option"
|
||||
echo 1>&2 "Try \`$0 --help' for more information"
|
||||
exit 1
|
||||
;;
|
||||
|
||||
esac
|
||||
|
||||
# normalize program name to check for.
|
||||
program=`echo "$1" | sed '
|
||||
s/^gnu-//; t
|
||||
s/^gnu//; t
|
||||
s/^g//; t'`
|
||||
|
||||
# Now exit if we have it, but it failed. Also exit now if we
|
||||
# don't have it and --version was passed (most likely to detect
|
||||
# the program). This is about non-GNU programs, so use $1 not
|
||||
# $program.
|
||||
case $1 in
|
||||
lex*|yacc*)
|
||||
# Not GNU programs, they don't have --version.
|
||||
;;
|
||||
|
||||
*)
|
||||
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
|
||||
# We have it, but it failed.
|
||||
exit 1
|
||||
elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
|
||||
# Could not run --version or --help. This is probably someone
|
||||
# running `$TOOL --version' or `$TOOL --help' to check whether
|
||||
# $TOOL exists and not knowing $TOOL uses missing.
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
# If it does not exist, or fails to run (possibly an outdated version),
|
||||
# try to emulate it.
|
||||
case $program in
|
||||
aclocal*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified \`acinclude.m4' or \`${configure_ac}'. You might want
|
||||
to install the \`Automake' and \`Perl' packages. Grab them from
|
||||
any GNU archive site."
|
||||
touch aclocal.m4
|
||||
;;
|
||||
|
||||
autoconf*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified \`${configure_ac}'. You might want to install the
|
||||
\`Autoconf' and \`GNU m4' packages. Grab them from any GNU
|
||||
archive site."
|
||||
touch configure
|
||||
;;
|
||||
|
||||
autoheader*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified \`acconfig.h' or \`${configure_ac}'. You might want
|
||||
to install the \`Autoconf' and \`GNU m4' packages. Grab them
|
||||
from any GNU archive site."
|
||||
files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
|
||||
test -z "$files" && files="config.h"
|
||||
touch_files=
|
||||
for f in $files; do
|
||||
case $f in
|
||||
*:*) touch_files="$touch_files "`echo "$f" |
|
||||
sed -e 's/^[^:]*://' -e 's/:.*//'`;;
|
||||
*) touch_files="$touch_files $f.in";;
|
||||
esac
|
||||
done
|
||||
touch $touch_files
|
||||
;;
|
||||
|
||||
automake*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
|
||||
You might want to install the \`Automake' and \`Perl' packages.
|
||||
Grab them from any GNU archive site."
|
||||
find . -type f -name Makefile.am -print |
|
||||
sed 's/\.am$/.in/' |
|
||||
while read f; do touch "$f"; done
|
||||
;;
|
||||
|
||||
autom4te*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is needed, but is $msg.
|
||||
You might have modified some files without having the
|
||||
proper tools for further handling them.
|
||||
You can get \`$1' as part of \`Autoconf' from any GNU
|
||||
archive site."
|
||||
|
||||
file=`echo "$*" | sed -n "$sed_output"`
|
||||
test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
|
||||
if test -f "$file"; then
|
||||
touch $file
|
||||
else
|
||||
test -z "$file" || exec >$file
|
||||
echo "#! /bin/sh"
|
||||
echo "# Created by GNU Automake missing as a replacement of"
|
||||
echo "# $ $@"
|
||||
echo "exit 0"
|
||||
chmod +x $file
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
bison*|yacc*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' $msg. You should only need it if
|
||||
you modified a \`.y' file. You may need the \`Bison' package
|
||||
in order for those modifications to take effect. You can get
|
||||
\`Bison' from any GNU archive site."
|
||||
rm -f y.tab.c y.tab.h
|
||||
if test $# -ne 1; then
|
||||
eval LASTARG=\${$#}
|
||||
case $LASTARG in
|
||||
*.y)
|
||||
SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
|
||||
if test -f "$SRCFILE"; then
|
||||
cp "$SRCFILE" y.tab.c
|
||||
fi
|
||||
SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
|
||||
if test -f "$SRCFILE"; then
|
||||
cp "$SRCFILE" y.tab.h
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
if test ! -f y.tab.h; then
|
||||
echo >y.tab.h
|
||||
fi
|
||||
if test ! -f y.tab.c; then
|
||||
echo 'main() { return 0; }' >y.tab.c
|
||||
fi
|
||||
;;
|
||||
|
||||
lex*|flex*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified a \`.l' file. You may need the \`Flex' package
|
||||
in order for those modifications to take effect. You can get
|
||||
\`Flex' from any GNU archive site."
|
||||
rm -f lex.yy.c
|
||||
if test $# -ne 1; then
|
||||
eval LASTARG=\${$#}
|
||||
case $LASTARG in
|
||||
*.l)
|
||||
SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
|
||||
if test -f "$SRCFILE"; then
|
||||
cp "$SRCFILE" lex.yy.c
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
if test ! -f lex.yy.c; then
|
||||
echo 'main() { return 0; }' >lex.yy.c
|
||||
fi
|
||||
;;
|
||||
|
||||
help2man*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified a dependency of a manual page. You may need the
|
||||
\`Help2man' package in order for those modifications to take
|
||||
effect. You can get \`Help2man' from any GNU archive site."
|
||||
|
||||
file=`echo "$*" | sed -n "$sed_output"`
|
||||
test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
|
||||
if test -f "$file"; then
|
||||
touch $file
|
||||
else
|
||||
test -z "$file" || exec >$file
|
||||
echo ".ab help2man is required to generate this page"
|
||||
exit $?
|
||||
fi
|
||||
;;
|
||||
|
||||
makeinfo*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is $msg. You should only need it if
|
||||
you modified a \`.texi' or \`.texinfo' file, or any other file
|
||||
indirectly affecting the aspect of the manual. The spurious
|
||||
call might also be the consequence of using a buggy \`make' (AIX,
|
||||
DU, IRIX). You might want to install the \`Texinfo' package or
|
||||
the \`GNU make' package. Grab either from any GNU archive site."
|
||||
# The file to touch is that specified with -o ...
|
||||
file=`echo "$*" | sed -n "$sed_output"`
|
||||
test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
|
||||
if test -z "$file"; then
|
||||
# ... or it is the one specified with @setfilename ...
|
||||
infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
|
||||
file=`sed -n '
|
||||
/^@setfilename/{
|
||||
s/.* \([^ ]*\) *$/\1/
|
||||
p
|
||||
q
|
||||
}' $infile`
|
||||
# ... or it is derived from the source name (dir/f.texi becomes f.info)
|
||||
test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info
|
||||
fi
|
||||
# If the file does not exist, the user really needs makeinfo;
|
||||
# let's fail without touching anything.
|
||||
test -f $file || exit 1
|
||||
touch $file
|
||||
;;
|
||||
|
||||
*)
|
||||
echo 1>&2 "\
|
||||
WARNING: \`$1' is needed, and is $msg.
|
||||
You might have modified some files without having the
|
||||
proper tools for further handling them. Check the \`README' file,
|
||||
it often tells you about the needed prerequisites for installing
|
||||
this package. You may also peek at any GNU archive site, in case
|
||||
some other package would contain this missing \`$1' program."
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
||||
-226
@@ -1,226 +0,0 @@
|
||||
#! /bin/sh
|
||||
# ylwrap - wrapper for lex/yacc invocations.
|
||||
|
||||
scriptversion=2011-08-25.18; # UTC
|
||||
|
||||
# Copyright (C) 1996, 1997, 1998, 1999, 2001, 2002, 2003, 2004, 2005,
|
||||
# 2007, 2009, 2010, 2011 Free Software Foundation, Inc.
|
||||
#
|
||||
# Written by Tom Tromey <tromey@cygnus.com>.
|
||||
#
|
||||
# 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
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# This file is maintained in Automake, please report
|
||||
# bugs to <bug-automake@gnu.org> or send patches to
|
||||
# <automake-patches@gnu.org>.
|
||||
|
||||
case "$1" in
|
||||
'')
|
||||
echo "$0: No files given. Try \`$0 --help' for more information." 1>&2
|
||||
exit 1
|
||||
;;
|
||||
--basedir)
|
||||
basedir=$2
|
||||
shift 2
|
||||
;;
|
||||
-h|--h*)
|
||||
cat <<\EOF
|
||||
Usage: ylwrap [--help|--version] INPUT [OUTPUT DESIRED]... -- PROGRAM [ARGS]...
|
||||
|
||||
Wrapper for lex/yacc invocations, renaming files as desired.
|
||||
|
||||
INPUT is the input file
|
||||
OUTPUT is one file PROG generates
|
||||
DESIRED is the file we actually want instead of OUTPUT
|
||||
PROGRAM is program to run
|
||||
ARGS are passed to PROG
|
||||
|
||||
Any number of OUTPUT,DESIRED pairs may be used.
|
||||
|
||||
Report bugs to <bug-automake@gnu.org>.
|
||||
EOF
|
||||
exit $?
|
||||
;;
|
||||
-v|--v*)
|
||||
echo "ylwrap $scriptversion"
|
||||
exit $?
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
# The input.
|
||||
input="$1"
|
||||
shift
|
||||
case "$input" in
|
||||
[\\/]* | ?:[\\/]*)
|
||||
# Absolute path; do nothing.
|
||||
;;
|
||||
*)
|
||||
# Relative path. Make it absolute.
|
||||
input="`pwd`/$input"
|
||||
;;
|
||||
esac
|
||||
|
||||
pairlist=
|
||||
while test "$#" -ne 0; do
|
||||
if test "$1" = "--"; then
|
||||
shift
|
||||
break
|
||||
fi
|
||||
pairlist="$pairlist $1"
|
||||
shift
|
||||
done
|
||||
|
||||
# The program to run.
|
||||
prog="$1"
|
||||
shift
|
||||
# Make any relative path in $prog absolute.
|
||||
case "$prog" in
|
||||
[\\/]* | ?:[\\/]*) ;;
|
||||
*[\\/]*) prog="`pwd`/$prog" ;;
|
||||
esac
|
||||
|
||||
# FIXME: add hostname here for parallel makes that run commands on
|
||||
# other machines. But that might take us over the 14-char limit.
|
||||
dirname=ylwrap$$
|
||||
do_exit="cd '`pwd`' && rm -rf $dirname > /dev/null 2>&1;"' (exit $ret); exit $ret'
|
||||
trap "ret=129; $do_exit" 1
|
||||
trap "ret=130; $do_exit" 2
|
||||
trap "ret=141; $do_exit" 13
|
||||
trap "ret=143; $do_exit" 15
|
||||
mkdir $dirname || exit 1
|
||||
|
||||
cd $dirname
|
||||
|
||||
case $# in
|
||||
0) "$prog" "$input" ;;
|
||||
*) "$prog" "$@" "$input" ;;
|
||||
esac
|
||||
ret=$?
|
||||
|
||||
if test $ret -eq 0; then
|
||||
set X $pairlist
|
||||
shift
|
||||
first=yes
|
||||
# Since DOS filename conventions don't allow two dots,
|
||||
# the DOS version of Bison writes out y_tab.c instead of y.tab.c
|
||||
# and y_tab.h instead of y.tab.h. Test to see if this is the case.
|
||||
y_tab_nodot="no"
|
||||
if test -f y_tab.c || test -f y_tab.h; then
|
||||
y_tab_nodot="yes"
|
||||
fi
|
||||
|
||||
# The directory holding the input.
|
||||
input_dir=`echo "$input" | sed -e 's,\([\\/]\)[^\\/]*$,\1,'`
|
||||
# Quote $INPUT_DIR so we can use it in a regexp.
|
||||
# FIXME: really we should care about more than `.' and `\'.
|
||||
input_rx=`echo "$input_dir" | sed 's,\\\\,\\\\\\\\,g;s,\\.,\\\\.,g'`
|
||||
|
||||
while test "$#" -ne 0; do
|
||||
from="$1"
|
||||
# Handle y_tab.c and y_tab.h output by DOS
|
||||
if test $y_tab_nodot = "yes"; then
|
||||
if test $from = "y.tab.c"; then
|
||||
from="y_tab.c"
|
||||
else
|
||||
if test $from = "y.tab.h"; then
|
||||
from="y_tab.h"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if test -f "$from"; then
|
||||
# If $2 is an absolute path name, then just use that,
|
||||
# otherwise prepend `../'.
|
||||
case "$2" in
|
||||
[\\/]* | ?:[\\/]*) target="$2";;
|
||||
*) target="../$2";;
|
||||
esac
|
||||
|
||||
# We do not want to overwrite a header file if it hasn't
|
||||
# changed. This avoid useless recompilations. However the
|
||||
# parser itself (the first file) should always be updated,
|
||||
# because it is the destination of the .y.c rule in the
|
||||
# Makefile. Divert the output of all other files to a temporary
|
||||
# file so we can compare them to existing versions.
|
||||
if test $first = no; then
|
||||
realtarget="$target"
|
||||
target="tmp-`echo $target | sed s/.*[\\/]//g`"
|
||||
fi
|
||||
# Edit out `#line' or `#' directives.
|
||||
#
|
||||
# We don't want the resulting debug information to point at
|
||||
# an absolute srcdir; it is better for it to just mention the
|
||||
# .y file with no path.
|
||||
#
|
||||
# We want to use the real output file name, not yy.lex.c for
|
||||
# instance.
|
||||
#
|
||||
# We want the include guards to be adjusted too.
|
||||
FROM=`echo "$from" | sed \
|
||||
-e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'\
|
||||
-e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g'`
|
||||
TARGET=`echo "$2" | sed \
|
||||
-e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'\
|
||||
-e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g'`
|
||||
|
||||
sed -e "/^#/!b" -e "s,$input_rx,," -e "s,$from,$2," \
|
||||
-e "s,$FROM,$TARGET," "$from" >"$target" || ret=$?
|
||||
|
||||
# Check whether header files must be updated.
|
||||
if test $first = no; then
|
||||
if test -f "$realtarget" && cmp -s "$realtarget" "$target"; then
|
||||
echo "$2" is unchanged
|
||||
rm -f "$target"
|
||||
else
|
||||
echo updating "$2"
|
||||
mv -f "$target" "$realtarget"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
# A missing file is only an error for the first file. This
|
||||
# is a blatant hack to let us support using "yacc -d". If -d
|
||||
# is not specified, we don't want an error when the header
|
||||
# file is "missing".
|
||||
if test $first = yes; then
|
||||
ret=1
|
||||
fi
|
||||
fi
|
||||
shift
|
||||
shift
|
||||
first=no
|
||||
done
|
||||
else
|
||||
ret=$?
|
||||
fi
|
||||
|
||||
# Remove the directory.
|
||||
cd ..
|
||||
rm -rf $dirname
|
||||
|
||||
exit $ret
|
||||
|
||||
# Local Variables:
|
||||
# mode: shell-script
|
||||
# sh-indentation: 2
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
||||
+175
-20
@@ -1,28 +1,76 @@
|
||||
AC_INIT([jq], [1.3], [mu@netsoc.tcd.ie],
|
||||
[jq], [http://stedolan.github.com/jq/])
|
||||
m4_define([jq_version], m4_esyscmd_s([git describe --tags --dirty --match 'jq-*'|sed 's/^jq-//']))
|
||||
|
||||
AC_INIT([jq], [jq_version], [https://github.com/stedolan/jq/issues],
|
||||
[jq], [http://stedolan.github.io/jq])
|
||||
|
||||
m4_include([m4/ax_compare_version.m4])
|
||||
m4_include([m4/ax_prog_bison_version.m4])
|
||||
|
||||
dnl Created autoconf implementation thompson@dtosolutions, 26NOV12
|
||||
AC_PREREQ([2.61])
|
||||
AC_PREREQ([2.64])
|
||||
AC_CONFIG_AUX_DIR([config])
|
||||
AM_INIT_AUTOMAKE([parallel-tests foreign])
|
||||
AM_INIT_AUTOMAKE([1.11.2 parallel-tests foreign -Wall])
|
||||
AM_SILENT_RULES([yes])
|
||||
AM_PROG_AR
|
||||
AM_MAINTAINER_MODE([enable])
|
||||
AC_PROG_CC
|
||||
AC_PROG_CC_STDC
|
||||
AC_PROG_CPP_WERROR
|
||||
AC_PROG_YACC
|
||||
|
||||
AC_OBJEXT
|
||||
AC_EXEEXT
|
||||
LT_INIT([shared static win32-dll])
|
||||
AM_PROG_CC_C_O
|
||||
|
||||
|
||||
dnl couldn't use AM_PROG_LEX as it doesn't support header files like the
|
||||
dnl AC_PROG_YACC macros...
|
||||
|
||||
dnl
|
||||
dnl these program checks should probably be deleted
|
||||
dnl
|
||||
dnl check bison version
|
||||
|
||||
AC_PROG_LEX
|
||||
if test "x$LEX" != xflex; then
|
||||
LEX="$SHELL $missing_dir/missing flex"
|
||||
if test "$USE_MAINTAINER_MODE" = yes; then
|
||||
if test "$YACC" != "bison -y"; then
|
||||
AC_MSG_CHECKING([bison version])
|
||||
AC_MSG_RESULT([not bison])
|
||||
else
|
||||
AX_PROG_BISON_VERSION([3],
|
||||
[],
|
||||
[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
|
||||
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])],
|
||||
[ONIGURUMAPATHSET=1],
|
||||
[ONIGURUMAPATHSET=0])
|
||||
|
||||
if test $ONIGURUMAPATHSET = 1; then
|
||||
CFLAGS="$CFLAGS -I${with_oniguruma}/include"
|
||||
LDFLAGS="$LDFLAGS -L${with_oniguruma}/lib"
|
||||
fi
|
||||
|
||||
# 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
|
||||
if test $HAVE_ONIGURUMA != 1; then
|
||||
AC_MSG_NOTICE([Oniguruma was not found.])
|
||||
AC_MSG_NOTICE([ Try setting the location using '--with-oniguruma=PREFIX' ])
|
||||
else
|
||||
AC_DEFINE([HAVE_ONIGURUMA],1,[Define to 1 if Oniguruma is installed])
|
||||
fi
|
||||
|
||||
|
||||
@@ -31,18 +79,41 @@ AC_CHECK_PROGS(valgrind_cmd, valgrind)
|
||||
if test "x$valgrind_cmd" = "x" ; then
|
||||
AC_MSG_WARN([valgrind is required to test jq.])
|
||||
fi
|
||||
AC_CHECK_FUNCS(memmem)
|
||||
AC_CHECK_FUNCS(mkstemp)
|
||||
|
||||
|
||||
dnl Running tests with Valgrind is slow. It is faster to iterate on
|
||||
dnl code without Valgrind until tests pass, then enable Valgrind and
|
||||
dnl fix leaks.
|
||||
AC_ARG_ENABLE([valgrind],
|
||||
AC_HELP_STRING([--disable-valgrind], [do not run tests under Valgrind]))
|
||||
|
||||
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
|
||||
AC_ARG_ENABLE([docs],
|
||||
AC_HELP_STRING([--disable-docs], [don't build docs]))
|
||||
|
||||
dnl Don't attempt to build the error injection object (if there is no LD_PRELOAD support)
|
||||
AC_ARG_ENABLE([error-injection],
|
||||
AC_HELP_STRING([--enable-error-injection], [build and test with error injection]))
|
||||
|
||||
dnl Enable building all static
|
||||
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)
|
||||
AS_IF([test "x$bundle_cmd" = "x" || \
|
||||
! ( cd ${srcdir}/docs; "$bundle_cmd" check 2>/dev/null )],[
|
||||
AC_MSG_WARN([no bundler])
|
||||
cat <<EOF
|
||||
|
||||
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])
|
||||
cat <<EOF
|
||||
*****************************************************************
|
||||
* Ruby dependencies for building jq documentation not found. *
|
||||
* You can still build, install and hack on jq, but the manpage *
|
||||
@@ -50,16 +121,100 @@ AS_IF([test "x$enable_docs" != "xno"],[
|
||||
* See docs/README.md for how to install the docs dependencies. *
|
||||
*****************************************************************
|
||||
EOF
|
||||
enable_docs=no
|
||||
])
|
||||
jq_cv_ruby_deps=no
|
||||
])])
|
||||
|
||||
if test "x$jq_cv_ruby_deps" != "xyes"; then
|
||||
enable_docs=no
|
||||
fi
|
||||
])
|
||||
|
||||
AM_CONDITIONAL([ENABLE_VALGRIND], [test "x$enable_valgrind" != xno])
|
||||
AM_CONDITIONAL([ENABLE_GCOV], [test "x$enable_gcov" = xyes])
|
||||
AM_CONDITIONAL([ENABLE_DOCS], [test "x$enable_docs" != xno])
|
||||
AM_CONDITIONAL([ENABLE_ERROR_INJECTION], [test "x$enable_error_injection" = xyes])
|
||||
AM_CONDITIONAL([ENABLE_ALL_STATIC], [test "x$enable_all_static" = xyes])
|
||||
|
||||
AC_FUNC_ALLOCA
|
||||
|
||||
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([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([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>]])
|
||||
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>]])
|
||||
|
||||
AC_ARG_ENABLE([pthread-tls],
|
||||
[AC_HELP_STRING([--enable-pthread-tls],
|
||||
[Enable use of pthread thread local storage])],
|
||||
[],
|
||||
[enable_pthread_tls=no])
|
||||
|
||||
if test $enable_pthread_tls = yes; then
|
||||
AC_FIND_FUNC([pthread_key_create], [pthread], [#include <pthread.h>], [NULL, NULL])
|
||||
AC_FIND_FUNC([pthread_once], [pthread], [#include <pthread.h>], [NULL, NULL])
|
||||
AC_FIND_FUNC([atexit], [pthread], [#include <stdlib.h>], [NULL])
|
||||
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(atanh, [.5])
|
||||
AC_CHECK_MATH_FUNC(cbrt, [.5])
|
||||
AC_CHECK_MATH_FUNC(cos, [.5])
|
||||
AC_CHECK_MATH_FUNC(cosh, [.5])
|
||||
AC_CHECK_MATH_FUNC(exp2, [.5])
|
||||
AC_CHECK_MATH_FUNC(exp, [.5])
|
||||
AC_CHECK_MATH_FUNC(floor, [.5])
|
||||
AC_CHECK_MATH_FUNC(j0, [.5])
|
||||
AC_CHECK_MATH_FUNC(j1, [.5])
|
||||
AC_CHECK_MATH_FUNC(log10, [.5])
|
||||
AC_CHECK_MATH_FUNC(log2, [.5])
|
||||
AC_CHECK_MATH_FUNC(log, [.5])
|
||||
AC_CHECK_MATH_FUNC(sin, [.5])
|
||||
AC_CHECK_MATH_FUNC(sinh, [.5])
|
||||
AC_CHECK_MATH_FUNC(sqrt, [.5])
|
||||
AC_CHECK_MATH_FUNC(tan, [.5])
|
||||
AC_CHECK_MATH_FUNC(tanh, [.5])
|
||||
AC_CHECK_MATH_FUNC(tgamma, [.5])
|
||||
AC_CHECK_MATH_FUNC(y0, [.5])
|
||||
AC_CHECK_MATH_FUNC(y1, [.5])
|
||||
AC_CHECK_MATH_FUNC(pow, [2,2])
|
||||
AC_CHECK_MATH_FUNC(atan2, [.5,.5])
|
||||
AC_CHECK_MATH_FUNC(hypot, [.5,.5])
|
||||
AC_CHECK_MATH_FUNC(remainder, [3,2])
|
||||
|
||||
dnl Thread local storage
|
||||
have___thread=no
|
||||
AC_MSG_CHECKING(for thread-local storage)
|
||||
AC_LINK_IFELSE([AC_LANG_SOURCE([
|
||||
static __thread int x ;
|
||||
int main () { x = 123; return x; }
|
||||
])], have___thread=yes)
|
||||
if test $have___thread = yes; then
|
||||
AC_DEFINE([HAVE___THREAD],1,[Define to 1 if the system supports __thread])
|
||||
fi
|
||||
AC_MSG_RESULT($have___thread)
|
||||
|
||||
AC_C_BIGENDIAN(
|
||||
AC_DEFINE([IEEE_MC68k], 1, [machine is bigendian]),
|
||||
AC_DEFINE([IEEE_8087], 1, [machine is littleendian]),
|
||||
AC_MSG_ERROR(unknown endianess),
|
||||
AC_MSG_ERROR(universial endianess not supported)
|
||||
)
|
||||
|
||||
AC_SUBST([BUNDLER], ["$bundle_cmd"])
|
||||
|
||||
dnl AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CONFIG_HEADERS(config.h)
|
||||
AM_INIT_AUTOMAKE([-Wall])
|
||||
AC_CONFIG_MACRO_DIR([config/m4])
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
AC_OUTPUT
|
||||
|
||||
|
||||
+4
-13
@@ -75,7 +75,7 @@ $BINARIES = {
|
||||
|
||||
$BINARIES.each do |name, args|
|
||||
file "../build/#{name}" do |t|
|
||||
sh "MAKEFLAGS=-j4 ../scripts/crosscompile #{name} #{args}"
|
||||
sh "../scripts/crosscompile -j4 #{name} #{args}"
|
||||
end
|
||||
task :binaries => ["../build/#{name}"]
|
||||
end
|
||||
@@ -87,14 +87,9 @@ task :binaries => ["output/download"] do
|
||||
end
|
||||
|
||||
def load_manual
|
||||
YAML::ENGINE.yamler = 'syck'
|
||||
YAML::load(File.open("content/3.manual/manual.yml"))
|
||||
end
|
||||
|
||||
def jq_version
|
||||
YAML::load(File.open("site.yml"))["jq_version"]
|
||||
end
|
||||
|
||||
task :manpage do
|
||||
Tempfile.open "manpage" do |f|
|
||||
manual = load_manual
|
||||
@@ -141,14 +136,10 @@ task :mantests do
|
||||
end
|
||||
end
|
||||
|
||||
$TARBALL = "../jq-#{jq_version}.tar.gz"
|
||||
file $TARBALL do
|
||||
sh "cd ..; ./configure && make dist && make distclean"
|
||||
end
|
||||
|
||||
directory "output/download/source"
|
||||
task :tarball => ["output/download/source", $TARBALL] do
|
||||
sh "cp #{$TARBALL} output/download/source"
|
||||
task :tarball => ["output/download/source"] do
|
||||
sh "cd ..; ./configure && make dist && make distclean"
|
||||
sh "cp ../jq-*.tar.gz output/download/source"
|
||||
end
|
||||
|
||||
task :dist => [:build, :binaries, :tarball]
|
||||
+241
-160
@@ -2,123 +2,205 @@ headline: Tutorial
|
||||
body:
|
||||
- text: |
|
||||
|
||||
Twitter has a JSON API, so let's play with that. This URL gets
|
||||
us the last 5 tweets about JSON:
|
||||
|
||||
- command: "curl 'http://search.twitter.com/search.json?q=json&rpp=5&include_entities=true'"
|
||||
GitHub has a JSON API, so let's play with that. This URL gets
|
||||
us the last 5 commits from the jq repo:
|
||||
|
||||
- command: "curl 'https://api.github.com/repos/stedolan/jq/commits?per_page=5'"
|
||||
result: |
|
||||
{"completed_in":0.108,"max_id":247677287108067328,"max_id_str":"247677287108067328","next_page":"?page=2&max_id=247677287108067328&q=json&rpp=5&include_entities=1","page":1,"query":"json","refresh_url":"?since_id=247677287108067328&q=json&include_entities=1","results":[{"created_at":"Mon, 17 Sep 2012 12:44:01 +0000","entities":{"hashtags":[],"urls":[{"url":"http:\/\/t.co\/XRvh1ZVw","expanded_url":"http:\/\/jase.im\/Ri7I0M","display_url":"jase.im\/Ri7I0M","indices":[112,132]}],"user_mentions":[{"screen_name":"imagemechanics","name":"Jason Cotterell","id":57271393,"id_str":"57271393","indices":[3,18]}]},"from_user":"_AaronNg","from_user_id":79771704,"from_user_id_str":"79771704","from_user_name":"NgChenChong","geo":null,"id":247677287108067328,"id_str":"247677287108067328","iso_language_code":"en","metadata":{"result_type":"recent"},"profile_image_url":"http:\/\/a0.twimg.com\/profile_images\/2523558403\/ek8mo4j4beq84iw28gjl_normal.jpeg","profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/2523558403\/ek8mo4j4beq84iw28gjl_normal.jpeg","source":"<a href="http:\/\/twitter.com\/">web<\/a>","text":"RT @imagemechanics: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas>.\nhttp:\/\/t.co\/XRvh1ZVw","to_user":null,"to_user_id":0,"to_user_id_str":"0","to_user_name":null}, ...
|
||||
[
|
||||
{
|
||||
"sha": "d25341478381063d1c76e81b3a52e0592a7c997f",
|
||||
"commit": {
|
||||
"author": {
|
||||
"name": "Stephen Dolan",
|
||||
"email": "mu@netsoc.tcd.ie",
|
||||
"date": "2013-06-22T16:30:59Z"
|
||||
},
|
||||
"committer": {
|
||||
"name": "Stephen Dolan",
|
||||
"email": "mu@netsoc.tcd.ie",
|
||||
"date": "2013-06-22T16:30:59Z"
|
||||
},
|
||||
"message": "Merge pull request #162 from stedolan/utf8-fixes\n\nUtf8 fixes. Closes #161",
|
||||
"tree": {
|
||||
"sha": "6ab697a8dfb5a96e124666bf6d6213822599fb40",
|
||||
"url": "https://api.github.com/repos/stedolan/jq/git/trees/6ab697a8dfb5a96e124666bf6d6213822599fb40"
|
||||
},
|
||||
"url": "https://api.github.com/repos/stedolan/jq/git/commits/d25341478381063d1c76e81b3a52e0592a7c997f",
|
||||
"comment_count": 0
|
||||
},
|
||||
"url": "https://api.github.com/repos/stedolan/jq/commits/d25341478381063d1c76e81b3a52e0592a7c997f",
|
||||
"html_url": "https://github.com/stedolan/jq/commit/d25341478381063d1c76e81b3a52e0592a7c997f",
|
||||
"comments_url": "https://api.github.com/repos/stedolan/jq/commits/d25341478381063d1c76e81b3a52e0592a7c997f/comments",
|
||||
"author": {
|
||||
"login": "stedolan",
|
||||
...
|
||||
|
||||
- text: |
|
||||
Github returns a ton of info here, and it's relatively hard to
|
||||
read as it is. To syntax highlight it and order the attributes we pipe
|
||||
it through jq, telling jq to just spit the input back at us using
|
||||
the expression `.`:
|
||||
|
||||
- command: "curl 'https://api.github.com/repos/stedolan/jq/commits?per_page=5' | jq '.'"
|
||||
result: |
|
||||
[
|
||||
{
|
||||
"parents": [
|
||||
{
|
||||
"html_url": "https://github.com/stedolan/jq/commit/54b9c9bdb225af5d886466d72f47eafc51acb4f7",
|
||||
"url": "https://api.github.com/repos/stedolan/jq/commits/54b9c9bdb225af5d886466d72f47eafc51acb4f7",
|
||||
"sha": "54b9c9bdb225af5d886466d72f47eafc51acb4f7"
|
||||
},
|
||||
{
|
||||
"html_url": "https://github.com/stedolan/jq/commit/8b1b503609c161fea4b003a7179b3fbb2dd4345a",
|
||||
"url": "https://api.github.com/repos/stedolan/jq/commits/8b1b503609c161fea4b003a7179b3fbb2dd4345a",
|
||||
"sha": "8b1b503609c161fea4b003a7179b3fbb2dd4345a"
|
||||
}
|
||||
],
|
||||
"committer": {
|
||||
"type": "User",
|
||||
"received_events_url": "https://api.github.com/users/stedolan/received_events",
|
||||
"events_url": "https://api.github.com/users/stedolan/events{/privacy}",
|
||||
"repos_url": "https://api.github.com/users/stedolan/repos",
|
||||
"organizations_url": "https://api.github.com/users/stedolan/orgs",
|
||||
...
|
||||
|
||||
- text: |
|
||||
|
||||
There's lots of info and no whitespace, so to make it a bit more
|
||||
legible we pipe it through jq, telling jq to just spit the input
|
||||
back at us using the expression `.`:
|
||||
There's still far too much info for our purposes, so we can try
|
||||
just grab the first commit:
|
||||
|
||||
|
||||
- command: "curl 'http://search.twitter.com/search.json?q=json&rpp=5&include_entities=true' | jq '.'"
|
||||
|
||||
- command: "curl 'https://api.github.com/repos/stedolan/jq/commits?per_page=5' | jq '.[0]'"
|
||||
result: |
|
||||
|
||||
{
|
||||
"since_id_str": "0",
|
||||
"since_id": 0,
|
||||
"results_per_page": 5,
|
||||
"completed_in": 0.108,
|
||||
"max_id": 247677287108067330,
|
||||
"max_id_str": "247677287108067328",
|
||||
"next_page": "?page=2&max_id=247677287108067328&q=json&rpp=5&include_entities=1",
|
||||
"page": 1,
|
||||
"query": "json",
|
||||
"refresh_url": "?since_id=247677287108067328&q=json&include_entities=1",
|
||||
"results": [
|
||||
"parents": [
|
||||
{
|
||||
"from_user_name": "NgChenChong",
|
||||
"from_user_id_str": "79771704",
|
||||
"from_user_id": 79771704,
|
||||
"from_user": "_AaronNg",
|
||||
"iso_language_code": "en",
|
||||
"text": "RT @imagemechanics: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas>.\nhttp://t.co/XRvh1ZVw",
|
||||
"to_user": null
|
||||
/* lots more fields... */
|
||||
"html_url": "https://github.com/stedolan/jq/commit/54b9c9bdb225af5d886466d72f47eafc51acb4f7",
|
||||
"url": "https://api.github.com/repos/stedolan/jq/commits/54b9c9bdb225af5d886466d72f47eafc51acb4f7",
|
||||
"sha": "54b9c9bdb225af5d886466d72f47eafc51acb4f7"
|
||||
},
|
||||
/* lots more results... */
|
||||
]
|
||||
{
|
||||
"html_url": "https://github.com/stedolan/jq/commit/8b1b503609c161fea4b003a7179b3fbb2dd4345a",
|
||||
"url": "https://api.github.com/repos/stedolan/jq/commits/8b1b503609c161fea4b003a7179b3fbb2dd4345a",
|
||||
"sha": "8b1b503609c161fea4b003a7179b3fbb2dd4345a"
|
||||
}
|
||||
],
|
||||
"committer": {
|
||||
"type": "User",
|
||||
"received_events_url": "https://api.github.com/users/stedolan/received_events",
|
||||
"events_url": "https://api.github.com/users/stedolan/events{/privacy}",
|
||||
"repos_url": "https://api.github.com/users/stedolan/repos",
|
||||
"organizations_url": "https://api.github.com/users/stedolan/orgs",
|
||||
"subscriptions_url": "https://api.github.com/users/stedolan/subscriptions",
|
||||
"starred_url": "https://api.github.com/users/stedolan/starred{/owner}{/repo}",
|
||||
"gists_url": "https://api.github.com/users/stedolan/gists{/gist_id}",
|
||||
"login": "stedolan",
|
||||
"id": 79765,
|
||||
"avatar_url": "https://1.gravatar.com/avatar/31de909d8e55dd07ed782d92ece59842?d=https%3A%2F%2Fidenticons.github.com%2Ffc5b6765b1c9cfaecea48ae71df4d279.png",
|
||||
"gravatar_id": "31de909d8e55dd07ed782d92ece59842",
|
||||
"url": "https://api.github.com/users/stedolan",
|
||||
"html_url": "https://github.com/stedolan",
|
||||
"followers_url": "https://api.github.com/users/stedolan/followers",
|
||||
"following_url": "https://api.github.com/users/stedolan/following{/other_user}"
|
||||
},
|
||||
"author": {
|
||||
"type": "User",
|
||||
"received_events_url": "https://api.github.com/users/stedolan/received_events",
|
||||
"events_url": "https://api.github.com/users/stedolan/events{/privacy}",
|
||||
"repos_url": "https://api.github.com/users/stedolan/repos",
|
||||
"organizations_url": "https://api.github.com/users/stedolan/orgs",
|
||||
"subscriptions_url": "https://api.github.com/users/stedolan/subscriptions",
|
||||
"starred_url": "https://api.github.com/users/stedolan/starred{/owner}{/repo}",
|
||||
"gists_url": "https://api.github.com/users/stedolan/gists{/gist_id}",
|
||||
"login": "stedolan",
|
||||
"id": 79765,
|
||||
"avatar_url": "https://1.gravatar.com/avatar/31de909d8e55dd07ed782d92ece59842?d=https%3A%2F%2Fidenticons.github.com%2Ffc5b6765b1c9cfaecea48ae71df4d279.png",
|
||||
"gravatar_id": "31de909d8e55dd07ed782d92ece59842",
|
||||
"url": "https://api.github.com/users/stedolan",
|
||||
"html_url": "https://github.com/stedolan",
|
||||
"followers_url": "https://api.github.com/users/stedolan/followers",
|
||||
"following_url": "https://api.github.com/users/stedolan/following{/other_user}"
|
||||
},
|
||||
"comments_url": "https://api.github.com/repos/stedolan/jq/commits/d25341478381063d1c76e81b3a52e0592a7c997f/comments",
|
||||
"html_url": "https://github.com/stedolan/jq/commit/d25341478381063d1c76e81b3a52e0592a7c997f",
|
||||
"url": "https://api.github.com/repos/stedolan/jq/commits/d25341478381063d1c76e81b3a52e0592a7c997f",
|
||||
"commit": {
|
||||
"comment_count": 0,
|
||||
"url": "https://api.github.com/repos/stedolan/jq/git/commits/d25341478381063d1c76e81b3a52e0592a7c997f",
|
||||
"tree": {
|
||||
"url": "https://api.github.com/repos/stedolan/jq/git/trees/6ab697a8dfb5a96e124666bf6d6213822599fb40",
|
||||
"sha": "6ab697a8dfb5a96e124666bf6d6213822599fb40"
|
||||
},
|
||||
"message": "Merge pull request #162 from stedolan/utf8-fixes\n\nUtf8 fixes. Closes #161",
|
||||
"committer": {
|
||||
"date": "2013-06-22T16:30:59Z",
|
||||
"email": "mu@netsoc.tcd.ie",
|
||||
"name": "Stephen Dolan"
|
||||
},
|
||||
"author": {
|
||||
"date": "2013-06-22T16:30:59Z",
|
||||
"email": "mu@netsoc.tcd.ie",
|
||||
"name": "Stephen Dolan"
|
||||
}
|
||||
},
|
||||
"sha": "d25341478381063d1c76e81b3a52e0592a7c997f"
|
||||
}
|
||||
|
||||
|
||||
- text: |
|
||||
|
||||
Let's pull out the first tweet:
|
||||
|
||||
|
||||
- command: "curl 'http://search.twitter.com/search.json?q=json&rpp=5&include_entities=true' | jq '.results[0]'"
|
||||
result: |
|
||||
{
|
||||
"from_user_name": "NgChenChong",
|
||||
"from_user_id_str": "79771704",
|
||||
"from_user_id": 79771704,
|
||||
"from_user": "_AaronNg",
|
||||
"iso_language_code": "en",
|
||||
"text": "RT @imagemechanics: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas>.\nhttp://t.co/XRvh1ZVw",
|
||||
"to_user": null
|
||||
/* lots more fields... */
|
||||
},
|
||||
|
||||
- text: |
|
||||
|
||||
For the rest of the examples, I'll leave out the `curl` command - it's not going to change.
|
||||
|
||||
|
||||
There's still a lot of info we don't care about there, so we'll
|
||||
restrict it down to the most interesting fields.
|
||||
|
||||
|
||||
- command: "jq '.results[0] | {from_user, text}'"
|
||||
|
||||
- command: "jq '.[0] | {message: .commit.message, name: .commit.committer.name}'"
|
||||
result: |
|
||||
{
|
||||
"text": "RT @imagemechanics: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas>.\nhttp://t.co/XRvh1ZVw",
|
||||
"from_user": "_AaronNg"
|
||||
"name": "Stephen Dolan",
|
||||
"message": "Merge pull request #162 from stedolan/utf8-fixes\n\nUtf8 fixes. Closes #161"
|
||||
}
|
||||
|
||||
|
||||
- text: |
|
||||
|
||||
|
||||
The `|` operator in jq feeds the output of one filter
|
||||
(`.results[0]` which gets the first element of the results
|
||||
array) into the input of another (`{from_user, text}` which
|
||||
builds an object of those two fields).
|
||||
|
||||
Now let's get the rest of the tweets:
|
||||
|
||||
|
||||
(`.[0]` which gets the first element of the array in the response)
|
||||
into the input of another (`{...}` which
|
||||
builds an object out of those fields). You can access nested attributes,
|
||||
such as `.commit.message`:
|
||||
|
||||
- command: "jq '.results[] | {from_user, text}'"
|
||||
Now let's get the rest of the commits:
|
||||
|
||||
|
||||
- command: "jq '.[] | {message: .commit.message, name: .commit.committer.name}'"
|
||||
result: |
|
||||
{
|
||||
"text": "RT @imagemechanics: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas>.\nhttp://t.co/XRvh1ZVw",
|
||||
"from_user": "_AaronNg"
|
||||
"name": "Stephen Dolan",
|
||||
"message": "Merge pull request #162 from stedolan/utf8-fixes\n\nUtf8 fixes. Closes #161"
|
||||
}
|
||||
{
|
||||
"text": "RT @_kud: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas> -- http://t.co/Lhp92IqD",
|
||||
"from_user": "blouerat"
|
||||
"name": "Stephen Dolan",
|
||||
"message": "Reject all overlong UTF8 sequences."
|
||||
}
|
||||
{
|
||||
"text": "Dynamic Forms Mobile App by pmadiset: Few Forms details are hosted on our server in the form of JSON. This JSON ... http://t.co/7KALdWaX",
|
||||
"from_user": "cebu_iphone"
|
||||
"name": "Stephen Dolan",
|
||||
"message": "Fix various UTF8 parsing bugs.\n\nIn particular, parse bad UTF8 by replacing the broken bits with U+FFFD\nand resychronise correctly after broken sequences."
|
||||
}
|
||||
{
|
||||
"text": "iPhone 5 website insanity - Video compressing using JPEG, JSON, and <canvas> http://t.co/28Jesbio (Oh #Apple, U So #Awesome...)",
|
||||
"from_user": "dieswaytoofast"
|
||||
"name": "Stephen Dolan",
|
||||
"message": "Fix example in manual for `floor`. See #155."
|
||||
}
|
||||
{
|
||||
"text": "RT @umutm: A very nice web-based JSON editor - http://t.co/M70snaIf",
|
||||
"from_user": "Leolik"
|
||||
"name": "Nicolas Williams",
|
||||
"message": "Document floor"
|
||||
}
|
||||
|
||||
|
||||
|
||||
- text: |
|
||||
|
||||
`.results[]` returns each element of the results array, one at a
|
||||
time, which are all fed into `{from_user, text}`.
|
||||
|
||||
`.[]` returns each element of the array returned in the response,
|
||||
one at a time, which are all fed into `{message: .commit.message, name: .commit.committer.name}`.
|
||||
|
||||
|
||||
Data in jq is represented as streams of JSON values - every jq
|
||||
@@ -128,109 +210,108 @@ body:
|
||||
Streams are serialised by just separating JSON values with
|
||||
whitespace. This is a `cat`-friendly format - you can just join
|
||||
two JSON streams together and get a valid JSON stream.
|
||||
|
||||
|
||||
If you want to get the output as a single array, you can tell jq to
|
||||
"collect" all of the answers by wrapping the filter in square
|
||||
brackets:
|
||||
|
||||
- command: "jq '[.results[] | {from_user, text}]'"
|
||||
- command: "jq '[.[] | {message: .commit.message, name: .commit.committer.name}]'"
|
||||
result: |
|
||||
[
|
||||
{
|
||||
"text": "RT @imagemechanics: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas>.\nhttp://t.co/XRvh1ZVw",
|
||||
"from_user": "_AaronNg"
|
||||
"name": "Stephen Dolan",
|
||||
"message": "Merge pull request #162 from stedolan/utf8-fixes\n\nUtf8 fixes. Closes #161"
|
||||
},
|
||||
{
|
||||
"text": "RT @_kud: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas> -- http://t.co/Lhp92IqD",
|
||||
"from_user": "blouerat"
|
||||
"name": "Stephen Dolan",
|
||||
"message": "Reject all overlong UTF8 sequences."
|
||||
},
|
||||
{
|
||||
"text": "Dynamic Forms Mobile App by pmadiset: Few Forms details are hosted on our server in the form of JSON. This JSON ... http://t.co/7KALdWaX",
|
||||
"from_user": "cebu_iphone"
|
||||
"name": "Stephen Dolan",
|
||||
"message": "Fix various UTF8 parsing bugs.\n\nIn particular, parse bad UTF8 by replacing the broken bits with U+FFFD\nand resychronise correctly after broken sequences."
|
||||
},
|
||||
{
|
||||
"text": "iPhone 5 website insanity - Video compressing using JPEG, JSON, and <canvas> http://t.co/28Jesbio (Oh #Apple, U So #Awesome...)",
|
||||
"from_user": "dieswaytoofast"
|
||||
"name": "Stephen Dolan",
|
||||
"message": "Fix example in manual for `floor`. See #155."
|
||||
},
|
||||
{
|
||||
"text": "RT @umutm: A very nice web-based JSON editor - http://t.co/M70snaIf",
|
||||
"from_user": "Leolik"
|
||||
"name": "Nicolas Williams",
|
||||
"message": "Document floor"
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
- text: |
|
||||
|
||||
- - -
|
||||
|
||||
Next, let's try getting the URLs out of those API results as
|
||||
well. In each tweet, the Twitter API includes a field called
|
||||
"entities" which looks like this:
|
||||
|
||||
{
|
||||
"user_mentions": [],
|
||||
"urls": [{
|
||||
"indices": [83, 103],
|
||||
"display_url": "bit.ly/StniqT",
|
||||
"expanded_url": "http://bit.ly/StniqT",
|
||||
"url": "http://t.co/28Jesbio"
|
||||
}],
|
||||
"hashtags": [
|
||||
{"indices": [108, 114], "text": "Apple" },
|
||||
{"indices": [121, 129], "text": "Awesome"}
|
||||
]
|
||||
}
|
||||
Next, let's try getting the URLs of the parent commits out of the
|
||||
API results as well. In each commit, the GitHub API includes information
|
||||
about "parent" commits. There can be one or many.
|
||||
|
||||
We want to pull out all of the "url" fields inside that array of url
|
||||
objects, and make a simple list of strings to go along with the
|
||||
"from_user" and "text" fields we already have.
|
||||
|
||||
|
||||
"parents": [
|
||||
{
|
||||
"html_url": "https://github.com/stedolan/jq/commit/54b9c9bdb225af5d886466d72f47eafc51acb4f7",
|
||||
"url": "https://api.github.com/repos/stedolan/jq/commits/54b9c9bdb225af5d886466d72f47eafc51acb4f7",
|
||||
"sha": "54b9c9bdb225af5d886466d72f47eafc51acb4f7"
|
||||
},
|
||||
{
|
||||
"html_url": "https://github.com/stedolan/jq/commit/8b1b503609c161fea4b003a7179b3fbb2dd4345a",
|
||||
"url": "https://api.github.com/repos/stedolan/jq/commits/8b1b503609c161fea4b003a7179b3fbb2dd4345a",
|
||||
"sha": "8b1b503609c161fea4b003a7179b3fbb2dd4345a"
|
||||
}
|
||||
]
|
||||
|
||||
- command: "jq '.results[] | {from_user, text, urls: [.entities.urls[].url]}'"
|
||||
We want to pull out all of the "html_url" fields inside that array of parent
|
||||
commits and make a simple list of strings to go along with the
|
||||
"message" and "author" fields we already have.
|
||||
|
||||
|
||||
|
||||
- command: "jq '[.[] | {message: .commit.message, name: .commit.committer.name, parents: [.parents[].html_url]}]'"
|
||||
result: |
|
||||
{
|
||||
"urls": [
|
||||
"http://t.co/XRvh1ZVw"
|
||||
],
|
||||
"text": "RT @imagemechanics: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas>.\nhttp://t.co/XRvh1ZVw",
|
||||
"from_user": "_AaronNg"
|
||||
}
|
||||
{
|
||||
"urls": [
|
||||
"http://t.co/Lhp92IqD"
|
||||
],
|
||||
"text": "RT @_kud: iPhone 5 website teardown: How Apple compresses video using JPEG, JSON, and <canvas> -- http://t.co/Lhp92IqD",
|
||||
"from_user": "blouerat"
|
||||
}
|
||||
{
|
||||
"urls": [
|
||||
"http://t.co/7KALdWaX"
|
||||
],
|
||||
"text": "Dynamic Forms Mobile App by pmadiset: Few Forms details are hosted on our server in the form of JSON. This JSON ... http://t.co/7KALdWaX",
|
||||
"from_user": "cebu_iphone"
|
||||
}
|
||||
{
|
||||
"urls": [
|
||||
"http://t.co/28Jesbio"
|
||||
],
|
||||
"text": "iPhone 5 website insanity - Video compressing using JPEG, JSON, and <canvas> http://t.co/28Jesbio (Oh #Apple, U So #Awesome...)",
|
||||
"from_user": "dieswaytoofast"
|
||||
}
|
||||
{
|
||||
"urls": [
|
||||
"http://t.co/M70snaIf"
|
||||
],
|
||||
"text": "RT @umutm: A very nice web-based JSON editor - http://t.co/M70snaIf",
|
||||
"from_user": "Leolik"
|
||||
}
|
||||
|
||||
|
||||
[
|
||||
{
|
||||
"parents": [
|
||||
"https://github.com/stedolan/jq/commit/54b9c9bdb225af5d886466d72f47eafc51acb4f7",
|
||||
"https://github.com/stedolan/jq/commit/8b1b503609c161fea4b003a7179b3fbb2dd4345a"
|
||||
],
|
||||
"name": "Stephen Dolan",
|
||||
"message": "Merge pull request #162 from stedolan/utf8-fixes\n\nUtf8 fixes. Closes #161"
|
||||
},
|
||||
{
|
||||
"parents": [
|
||||
"https://github.com/stedolan/jq/commit/ff48bd6ec538b01d1057be8e93b94eef6914e9ef"
|
||||
],
|
||||
"name": "Stephen Dolan",
|
||||
"message": "Reject all overlong UTF8 sequences."
|
||||
},
|
||||
{
|
||||
"parents": [
|
||||
"https://github.com/stedolan/jq/commit/54b9c9bdb225af5d886466d72f47eafc51acb4f7"
|
||||
],
|
||||
"name": "Stephen Dolan",
|
||||
"message": "Fix various UTF8 parsing bugs.\n\nIn particular, parse bad UTF8 by replacing the broken bits with U+FFFD\nand resychronise correctly after broken sequences."
|
||||
},
|
||||
{
|
||||
"parents": [
|
||||
"https://github.com/stedolan/jq/commit/3dcdc582ea993afea3f5503a78a77675967ecdfa"
|
||||
],
|
||||
"name": "Stephen Dolan",
|
||||
"message": "Fix example in manual for `floor`. See #155."
|
||||
},
|
||||
{
|
||||
"parents": [
|
||||
"https://github.com/stedolan/jq/commit/7c4171d414f647ab08bcd20c76a4d8ed68d9c602"
|
||||
],
|
||||
"name": "Nicolas Williams",
|
||||
"message": "Document floor"
|
||||
}
|
||||
]
|
||||
|
||||
- text: |
|
||||
|
||||
Here we're making an object as before, but this time the urls
|
||||
field is being set to `[.entities.urls[].url]`, which collects
|
||||
all of the URLs defined in the entities object.
|
||||
|
||||
Here we're making an object as before, but this time the `parents`
|
||||
field is being set to `[.parents[].html_url]}]`, which collects
|
||||
all of the parent commit URLs defined in the parents object.
|
||||
|
||||
- text: |
|
||||
|
||||
@@ -238,4 +319,4 @@ body:
|
||||
|
||||
Here endeth the tutorial! There's lots more to play with, go
|
||||
read [the manual](../manual/) if you're interested, and [download
|
||||
jq](../download/) if you haven't already.
|
||||
jq](../download/) if you haven't already.
|
||||
@@ -16,7 +16,8 @@ body:
|
||||
|
||||
### Linux
|
||||
|
||||
* Binaries for [64-bit](linux64/jq) or [32-bit](linux32/jq) systems.
|
||||
* jq 1.4 binaries for [64-bit](linux64/jq) or [32-bit](linux32/jq) systems.
|
||||
* jq 1.3 binaries for [64-bit](linux64/jq-1.3/jq) or [32-bit](linux32/jq-1.3/jq) systems.
|
||||
|
||||
* For Arch users, a PKGBUILD for jq-1.1 is in the
|
||||
[AUR](https://aur.archlinux.org/packages.php?ID=63849), as
|
||||
@@ -27,31 +28,47 @@ body:
|
||||
manually by following instructions on [Arch Linux's
|
||||
Wiki](https://wiki.archlinux.org/index.php/Arch_UseRepository).
|
||||
|
||||
* jq 1.2 is [in Debian](http://packages.debian.org/jq)
|
||||
* jq 1.4 is [in Debian](http://packages.debian.org/jq)
|
||||
|
||||
* jq 1.3.2 is in the official Fedora repository. You can install using
|
||||
`sudo yum install jq`.
|
||||
|
||||
### OS X
|
||||
|
||||
* `brew install jq` using [homebrew](http://mxcl.github.io/homebrew/)
|
||||
* `brew install jq` using [homebrew](http://mxcl.github.com/homebrew/)
|
||||
|
||||
* Or, grab prebuilt [64-bit binaries](osx64/jq) or [32-bit
|
||||
* Or, grab prebuilt jq 1.4 [64-bit binaries](osx64/jq) or [32-bit
|
||||
binaries](osx32/jq)
|
||||
|
||||
* Or, grab prebuilt jq 1.3 [64-bit binaries](osx64/jq-1.3/jq) or [32-bit
|
||||
binaries](osx32/jq-1.3/jq)
|
||||
|
||||
### Solaris
|
||||
|
||||
* `pkgutil -i jq` in [OpenCSW](http://www.opencsw.org/p/jq) for Solaris 10+, Sparc and x86
|
||||
|
||||
* jq 1.4 executables for Solaris 11 [64-bit](solaris11-64/jq) or [32-bit](solaris11-32/jq)
|
||||
|
||||
### Windows
|
||||
|
||||
* Executables for [64-bit](win64/jq.exe) or [32-bit](win32/jq.exe)
|
||||
* `chocolatey install jq` using [Chocolatey NuGet](https://chocolatey.org/)
|
||||
|
||||
### From source on Linux or OS X
|
||||
* jq 1.4 executables for [64-bit](win64/jq.exe) or [32-bit](win32/jq.exe)
|
||||
|
||||
* [Source tarball for jq $JQ_VERSION](source/jq-$JQ_VERSION.tar.gz)
|
||||
* jq 1.3 executables for [64-bit](win64/jq-1.3/jq-1.3.exe) or [32-bit](win32/jq-1.3/jq-1.3.exe)
|
||||
|
||||
### From source on Linux, OS X, Cygwin, and other POSIX-like operating systems
|
||||
|
||||
* [Source tarball for jq 1.4](source/jq-1.4.tar.gz)
|
||||
|
||||
You can build it using the usual `./configure && make && sudo
|
||||
make install` rigmarole.
|
||||
|
||||
If you're interested in using the latest development version, try:
|
||||
If you're interested in using the lastest development version, try:
|
||||
|
||||
git clone https://github.com/stedolan/jq.git
|
||||
cd jq
|
||||
autoreconf
|
||||
autoreconf -i
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
@@ -72,19 +89,25 @@ body:
|
||||
On OS X, these are all included in Apple's command line tools, which
|
||||
can be installed from [Xcode](http://developer.apple.com/technologies/tools/).
|
||||
However, you may find that you need a newer version of Bison than the one provided
|
||||
by Apple. This can be found in [Homebrew](http://mxcl.github.io/homebrew/)
|
||||
by Apple. This can be found in [Homebrew](http://mxcl.github.com/homebrew/)
|
||||
or [MacPorts](http://macports.org/).
|
||||
|
||||
`flex` and `bison` are used to generate the lexer and parser for
|
||||
jq, and some python scripts generate the UTF8 encoding tables
|
||||
needed for JSON parsing.
|
||||
jq. For jq 1.5, neither is required if you use `./configure
|
||||
--disable-maintainer-mode` instead of `./configure`. This flag says to use
|
||||
the pre-generated lexer and parser that come with the code.
|
||||
|
||||
#### 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` from the docs/ subdirectory. You'll
|
||||
need a few Ruby dependencies, which can be installed by
|
||||
following the instructions in `docs/README.md`.
|
||||
[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`.
|
||||
|
||||
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
|
||||
build the manpage.
|
||||
|
||||
|
||||
+1790
-194
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -1,9 +1,9 @@
|
||||
headline: jq
|
||||
|
||||
blurb: |
|
||||
|
||||
|
||||
jq is a lightweight and flexible command-line JSON processor.
|
||||
|
||||
|
||||
body1: |
|
||||
|
||||
jq is like `sed` for JSON data - you can use it to slice and filter
|
||||
@@ -14,21 +14,37 @@ body2: |
|
||||
|
||||
jq is written in portable C, and it has zero runtime
|
||||
dependencies. You can download a single binary, `scp` it to a far away
|
||||
machine, and expect it to work.
|
||||
machine of the same type, and expect it to work.
|
||||
|
||||
body3: |
|
||||
|
||||
jq can mangle the data format that you have into the one that you
|
||||
want with very little effort, and the program to do so is often
|
||||
shorter and simpler than you'd expect.
|
||||
|
||||
|
||||
tail: |
|
||||
|
||||
Go read the [tutorial](tutorial/) for more, or the [manual](manual/)
|
||||
for *way* more.
|
||||
for *way* more. Ask questions over on
|
||||
[stackoverflow](http://stackoverflow.com/) using the [jq
|
||||
tag](http://stackoverflow.com/questions/tagged/jq), or on the
|
||||
[#jq](http://irc.lc/freenode/%23jq/) channel over on
|
||||
[Freenode](https://webchat.freenode.net/).
|
||||
|
||||
news:
|
||||
- date: 2015-01-01
|
||||
body: |
|
||||
|
||||
jq 1.5rc1 Get it on the
|
||||
[releases](https://github.com/stedolan/jq/releases) page.
|
||||
|
||||
- date: 2014-06-08
|
||||
body: |
|
||||
|
||||
jq 1.4 (finally) released! Get it on the [download](download/) page.
|
||||
|
||||
- date: 2013-05-19
|
||||
body: |
|
||||
|
||||
jq 1.3 (finally) released! Get it on the [download](download/) page.
|
||||
|
||||
jq 1.3 released.
|
||||
|
||||
@@ -9,7 +9,7 @@ reducing and otherwise mangling JSON documents.
|
||||
This version of `jq` was built without a manual, so this manpage is a
|
||||
stub. For full documentation of the `jq` language, see:
|
||||
|
||||
http://stedolan.github.com/jq
|
||||
http://stedolan.github.io/jq
|
||||
|
||||
## BUGS
|
||||
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
|
||||
# This line is modified by the Makefile. To change the version number,
|
||||
# edit the Autoconf version number at the top of configure.ac
|
||||
jq_version: "1.3"
|
||||
jq_version: "1.4-2-g15c4a7f-dirty"
|
||||
|
||||
root: '/jq'
|
||||
footer: |
|
||||
|
||||
Vendored
+10
-1
@@ -10,6 +10,7 @@
|
||||
<img src="{{root}}/jq.png" style="width: 400px; height: 220px; float: left; margin-right: 60px">
|
||||
<div id="blurb">
|
||||
{{blurb | markdownify}}
|
||||
|
||||
<div class="btn-group" style="float:right">
|
||||
<a class="btn btn-primary dropdown-toggle" data-toggle="dropdown" href="#">
|
||||
Download jq-{{jq_version}}
|
||||
@@ -18,10 +19,18 @@
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="{{root}}/download/linux64/jq">Linux (64-bit)</a></li>
|
||||
<li><a href="{{root}}/download/osx64/jq">OS X (64-bit)</a></li>
|
||||
<li><a href="{{root}}/download/">Other platforms and source</a></li>
|
||||
<li><a href="{{root}}/download/solaris11-64/jq">Solaris (64-bit)</a></li>
|
||||
<li><a href="{{root}}/download/win64/jq.exe">Windows (64-bit)</a></li>
|
||||
<li><a href="{{root}}/download/">Other platforms, older versions, and source</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="btn-group" style="float:right">
|
||||
<a class="btn btn-primary" href="http://jqplay.org">
|
||||
Try online at jqplay.org!
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div style="clear:both;"></div>
|
||||
</div>
|
||||
|
||||
Vendored
+15
-3
@@ -35,6 +35,17 @@
|
||||
}
|
||||
});
|
||||
});
|
||||
// add "Run" button to execute examples on jqplay.org
|
||||
$(function() {
|
||||
$.each($('table.manual-example'), 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 url = 'https://jqplay.org/jq?q=' + encodeURIComponent(q) +'&j=' + encodeURIComponent(j)
|
||||
var $last_tr = $value.find('tr:last');
|
||||
$last_tr.after('<tr><th><a href="' + url + '" class="btn btn-primary btn-small">Run</a></th><th></th></tr><tr><th></th><th></th></tr>');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<input type="text"
|
||||
class="input-medium search-query"
|
||||
@@ -46,6 +57,7 @@
|
||||
</div>
|
||||
<div class="span9">
|
||||
<h1>{{headline}}</h1>
|
||||
{{ history | markdownify }}
|
||||
{{ body | markdownify }}
|
||||
{% for section in sections %}
|
||||
<section id="{{section.title | sanitize}}">
|
||||
@@ -70,8 +82,8 @@
|
||||
|
||||
{% for example in entry.examples %}
|
||||
<table class="manual-example">
|
||||
<tr><th></th><td class="jqprogram">jq '{{example.program}}'</td></tr>
|
||||
<tr><th>Input</th><td>{{example.input}}</td></tr>
|
||||
<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>Output</th>
|
||||
@@ -85,7 +97,7 @@
|
||||
{% else %}
|
||||
<th></th>
|
||||
{% endif %}
|
||||
<td>{{output}}</td>
|
||||
<td>{{output | escape}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
||||
|
||||
Vendored
+2
@@ -17,6 +17,8 @@
|
||||
{% endfor %}
|
||||
<li><a href="https://github.com/stedolan/jq/issues">Issues</a></li>
|
||||
<li><a href="https://github.com/stedolan/jq">Source</a></li>
|
||||
<li><a href="http://jqplay.org">Try online!</a></li>
|
||||
<li><a href="https://raw.githubusercontent.com/stedolan/jq/master/NEWS">News</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
#ifndef EXEC_STACK_H
|
||||
#define EXEC_STACK_H
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "jv_alloc.h"
|
||||
|
||||
/*
|
||||
* The stack is a directed forest of variably sized blocks. Each block has a
|
||||
* "next" block which is at a higher memory address, or 0 if the block has no
|
||||
* "next" block. More than one block may have no "next" block. A block may be
|
||||
* the "next" block of more than one other block. Pushed blocks are added at
|
||||
* the low-address end of the stack.
|
||||
*
|
||||
* Stack pointers are negative integers that are offsets relative to "mem_end",
|
||||
* the end of the allocated region. The stack "bound" is the stack pointer of
|
||||
* the last block that would be able to fit in the currently allocated region.
|
||||
* The stack "limit" is the stack pointer of the last block currently in the
|
||||
* stack. The stack pointer of the "next" block is stored directly below each
|
||||
* block.
|
||||
*
|
||||
* <- mem_end = 0x100
|
||||
* 0xF8 +------------+
|
||||
* 0xF0 | |
|
||||
* 0xE8 +------------+ <- stack_ptr1 = -0x18
|
||||
* 0xE0 next = 0
|
||||
* 0xD8 +------------+
|
||||
* 0xD0 | |
|
||||
* 0xC8 | |
|
||||
* 0xC0 +------------+ <- stack_ptr2 = limit = -0x40
|
||||
* 0xB8 next = -0x18
|
||||
* 0xB0
|
||||
* 0xA8 <- bound = -0x58
|
||||
* 0xA0
|
||||
*/
|
||||
|
||||
struct determine_alignment {
|
||||
char x;
|
||||
union { int i; double d; uint64_t u64; size_t sz; void* ptr; } u;
|
||||
};
|
||||
enum {ALIGNMENT = offsetof(struct determine_alignment, u)};
|
||||
|
||||
static size_t align_round_up(size_t sz) {
|
||||
return ((sz + (ALIGNMENT - 1)) / ALIGNMENT) * ALIGNMENT;
|
||||
}
|
||||
|
||||
typedef int stack_ptr;
|
||||
|
||||
struct stack {
|
||||
char* mem_end; // one-past-the-end of allocated region
|
||||
stack_ptr bound;
|
||||
stack_ptr limit; // 0 - stack is empty
|
||||
};
|
||||
|
||||
static void stack_init(struct stack* s) {
|
||||
s->mem_end = 0;
|
||||
s->bound = ALIGNMENT;
|
||||
s->limit = 0;
|
||||
}
|
||||
|
||||
static void stack_reset(struct stack* s) {
|
||||
assert(s->limit == 0 && "stack freed while not empty");
|
||||
char* mem_start = s->mem_end - ( -s->bound + ALIGNMENT);
|
||||
free(mem_start);
|
||||
stack_init(s);
|
||||
}
|
||||
|
||||
static int stack_pop_will_free(struct stack* s, stack_ptr p) {
|
||||
return p == s->limit;
|
||||
}
|
||||
|
||||
static void* stack_block(struct stack* s, stack_ptr p) {
|
||||
return (void*)(s->mem_end + p);
|
||||
}
|
||||
|
||||
static stack_ptr* stack_block_next(struct stack* s, stack_ptr p) {
|
||||
return &((stack_ptr*)stack_block(s, p))[-1];
|
||||
}
|
||||
|
||||
static void stack_reallocate(struct stack* s, size_t sz) {
|
||||
int old_mem_length = -(s->bound) + ALIGNMENT;
|
||||
char* old_mem_start = s->mem_end - old_mem_length;
|
||||
|
||||
int new_mem_length = align_round_up((old_mem_length + sz + 256) * 2);
|
||||
char* new_mem_start = jv_mem_realloc(old_mem_start, new_mem_length);
|
||||
memmove(new_mem_start + (new_mem_length - old_mem_length),
|
||||
new_mem_start, old_mem_length);
|
||||
s->mem_end = new_mem_start + new_mem_length;
|
||||
s->bound = -(new_mem_length - ALIGNMENT);
|
||||
}
|
||||
|
||||
static stack_ptr stack_push_block(struct stack* s, stack_ptr p, size_t sz) {
|
||||
int alloc_sz = align_round_up(sz) + ALIGNMENT;
|
||||
stack_ptr r = s->limit - alloc_sz;
|
||||
if (r < s->bound) {
|
||||
stack_reallocate(s, alloc_sz);
|
||||
}
|
||||
s->limit = r;
|
||||
*stack_block_next(s, r) = p;
|
||||
return r;
|
||||
}
|
||||
|
||||
static stack_ptr stack_pop_block(struct stack* s, stack_ptr p, size_t sz) {
|
||||
stack_ptr r = *stack_block_next(s, p);
|
||||
if (p == s->limit) {
|
||||
int alloc_sz = align_round_up(sz) + ALIGNMENT;
|
||||
s->limit += alloc_sz;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
#endif
|
||||
@@ -1,20 +0,0 @@
|
||||
#ifndef EXECUTE_H
|
||||
#define EXECUTE_H
|
||||
#include "bytecode.h"
|
||||
|
||||
|
||||
|
||||
struct bytecode* jq_compile(const char* str);
|
||||
|
||||
/* args must be an array of the form [{name:"foo", value:"thing"}, {name:"bar",value:3}] */
|
||||
struct bytecode* jq_compile_args(const char* str, jv args);
|
||||
|
||||
typedef struct jq_state jq_state;
|
||||
enum {JQ_DEBUG_TRACE = 1};
|
||||
|
||||
void jq_init(struct bytecode* bc, jv value, jq_state **, int flags);
|
||||
jv jq_next(jq_state *);
|
||||
void jq_teardown(jq_state **);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -1,172 +0,0 @@
|
||||
#ifndef FORKABLE_STACK_H
|
||||
#define FORKABLE_STACK_H
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "jv_alloc.h"
|
||||
|
||||
struct forkable_stack_header {
|
||||
/* distance in bytes between this header and the header
|
||||
of the next object on the stack */
|
||||
int next_delta;
|
||||
};
|
||||
|
||||
#define FORKABLE_STACK_HEADER struct forkable_stack_header fk_header_
|
||||
|
||||
struct forkable_stack {
|
||||
// Stack grows down from stk+length
|
||||
char* stk;
|
||||
|
||||
// stk+length is just past end of allocated area
|
||||
// stk = jv_mem_alloc(length)
|
||||
int length;
|
||||
|
||||
// stk+pos is header of top object, or stk+length if empty
|
||||
int pos;
|
||||
|
||||
// everything after-or-including stk+savedlimit must be preserved
|
||||
int savedlimit;
|
||||
};
|
||||
|
||||
static void forkable_stack_check(struct forkable_stack* s) {
|
||||
assert(s->stk);
|
||||
assert(s->length > 0);
|
||||
assert(s->pos >= 0 && s->pos <= s->length);
|
||||
assert(s->savedlimit >= 0 && s->savedlimit <= s->length);
|
||||
}
|
||||
|
||||
static int forkable_stack_empty(struct forkable_stack* s) {
|
||||
return s->pos == s->length;
|
||||
}
|
||||
|
||||
static void forkable_stack_init(struct forkable_stack* s, size_t sz) {
|
||||
s->stk = jv_mem_alloc(sz);
|
||||
s->length = sz;
|
||||
s->pos = s->length;
|
||||
s->savedlimit = s->length;
|
||||
forkable_stack_check(s);
|
||||
}
|
||||
|
||||
static void forkable_stack_free(struct forkable_stack* s) {
|
||||
assert(forkable_stack_empty(s));
|
||||
assert(s->savedlimit == s->length);
|
||||
jv_mem_free(s->stk);
|
||||
s->stk = 0;
|
||||
}
|
||||
|
||||
static void* forkable_stack_push(struct forkable_stack* s, size_t sz_size) {
|
||||
assert(sz_size > 0 && sz_size % sizeof(struct forkable_stack_header) == 0);
|
||||
int size = (int)sz_size;
|
||||
forkable_stack_check(s);
|
||||
int curr = s->pos < s->savedlimit ? s->pos : s->savedlimit;
|
||||
if (curr - size < 0) {
|
||||
int oldlen = s->length;
|
||||
s->length = (size + oldlen + 1024) * 2;
|
||||
s->stk = jv_mem_realloc(s->stk, s->length);
|
||||
int shift = s->length - oldlen;
|
||||
memmove(s->stk + shift, s->stk, oldlen);
|
||||
s->pos += shift;
|
||||
s->savedlimit += shift;
|
||||
curr += shift;
|
||||
}
|
||||
int newpos = curr - size;
|
||||
assert(newpos < s->pos);
|
||||
void* ret = (void*)(s->stk + newpos);
|
||||
((struct forkable_stack_header*)ret)->next_delta = s->pos - newpos;
|
||||
s->pos = newpos;
|
||||
forkable_stack_check(s);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void* forkable_stack_peek(struct forkable_stack* s) {
|
||||
assert(!forkable_stack_empty(s));
|
||||
forkable_stack_check(s);
|
||||
return (void*)(s->stk + s->pos);
|
||||
}
|
||||
|
||||
static void* forkable_stack_peek_next(struct forkable_stack* s, void* top) {
|
||||
forkable_stack_check(s);
|
||||
struct forkable_stack_header* elem = top;
|
||||
int elempos = (char*)top - s->stk;
|
||||
assert(elempos >= 0 && elempos < s->length);
|
||||
assert(elempos + elem->next_delta <= s->length);
|
||||
if (elempos + elem->next_delta < s->length) {
|
||||
return (void*)(s->stk + elempos + elem->next_delta);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns 1 if the next forkable_stack_pop will permanently remove an
|
||||
// object from the stack (i.e. the top object was not saved with a fork)
|
||||
static int forkable_stack_pop_will_free(struct forkable_stack* s) {
|
||||
return s->pos < s->savedlimit;
|
||||
}
|
||||
|
||||
static void forkable_stack_pop(struct forkable_stack* s) {
|
||||
forkable_stack_check(s);
|
||||
struct forkable_stack_header* elem = forkable_stack_peek(s);
|
||||
int reclaim_upto = s->pos + elem->next_delta < s->savedlimit ?
|
||||
s->pos + elem->next_delta : s->savedlimit;
|
||||
assert(reclaim_upto <= s->length);
|
||||
int reclaimed = reclaim_upto > s->pos ? reclaim_upto - s->pos : 0;
|
||||
assert(0 <= reclaimed && s->pos + reclaimed <= s->length);
|
||||
// s->pos <= i < reclaim_upto means that position i is to be freed
|
||||
assert((reclaimed > 0) == forkable_stack_pop_will_free(s));
|
||||
int new_pos = s->pos + elem->next_delta;
|
||||
jv_mem_invalidate(elem, reclaimed);
|
||||
s->pos = new_pos;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct forkable_stack_state {
|
||||
// We save the previous pos, savedlimit as
|
||||
// length-pos, length-savedlimit since these
|
||||
// values are stable across stack reallocations.
|
||||
int prev_pos_delta, prev_limit_delta;
|
||||
};
|
||||
|
||||
static void forkable_stack_save(struct forkable_stack* s, struct forkable_stack_state* state) {
|
||||
forkable_stack_check(s);
|
||||
state->prev_pos_delta = s->length - s->pos;
|
||||
state->prev_limit_delta = s->length - s->savedlimit;
|
||||
if (s->pos < s->savedlimit) s->savedlimit = s->pos;
|
||||
}
|
||||
|
||||
static void forkable_stack_switch(struct forkable_stack* s, struct forkable_stack_state* state) {
|
||||
forkable_stack_check(s);
|
||||
int curr_pos = s->pos;
|
||||
s->pos = s->length - state->prev_pos_delta;
|
||||
state->prev_pos_delta = s->length - curr_pos;
|
||||
|
||||
int curr_limit = s->savedlimit;
|
||||
if (curr_pos < curr_limit) s->savedlimit = curr_pos;
|
||||
//state->prevlimit = curr_limit; FIXME clean up
|
||||
forkable_stack_check(s);
|
||||
}
|
||||
|
||||
static void forkable_stack_restore(struct forkable_stack* s, struct forkable_stack_state* state) {
|
||||
forkable_stack_check(s);
|
||||
assert(s->savedlimit <= s->length - state->prev_pos_delta);
|
||||
assert(s->savedlimit <= s->length - state->prev_limit_delta);
|
||||
s->pos = s->length - state->prev_pos_delta;
|
||||
s->savedlimit = s->length - state->prev_limit_delta;
|
||||
forkable_stack_check(s);
|
||||
}
|
||||
|
||||
typedef int stack_idx;
|
||||
|
||||
static stack_idx forkable_stack_to_idx(struct forkable_stack* s, void* ptr) {
|
||||
char* item = ptr;
|
||||
int pos = item - s->stk;
|
||||
assert(pos >= 0 && pos < s->length);
|
||||
return s->length - pos;
|
||||
}
|
||||
|
||||
static void* forkable_stack_from_idx(struct forkable_stack* s, stack_idx idx) {
|
||||
assert(idx >= 1 && idx <= s->length);
|
||||
return &s->stk[s->length - idx];
|
||||
}
|
||||
|
||||
#endif
|
||||
-104
@@ -1,104 +0,0 @@
|
||||
#ifndef FRAME_LAYOUT_H
|
||||
#include "forkable_stack.h"
|
||||
#include "bytecode.h"
|
||||
|
||||
struct closure {
|
||||
struct bytecode* bc;
|
||||
stack_idx env;
|
||||
};
|
||||
|
||||
struct continuation {
|
||||
struct bytecode* bc;
|
||||
stack_idx env;
|
||||
uint16_t* retaddr;
|
||||
};
|
||||
|
||||
typedef union frame_elem {
|
||||
FORKABLE_STACK_HEADER;
|
||||
struct continuation cont;
|
||||
struct closure closure;
|
||||
jv jsonval;
|
||||
} *frame_ptr;
|
||||
|
||||
/*
|
||||
* Frame layout
|
||||
* fr[0] - FORKABLE_STACK_HEADER (next pointer)
|
||||
* fr[1] - self (used to store return addresses, etc)
|
||||
* fr[2...nclosures+2] - closure params
|
||||
* fr[nclosures+2..nclosures+nlocals+2] - local variables
|
||||
*/
|
||||
|
||||
static int frame_size(struct bytecode* bc) {
|
||||
return sizeof(union frame_elem) * (bc->nclosures + bc->nlocals + 2);
|
||||
}
|
||||
|
||||
static struct continuation* frame_self(frame_ptr fr) {
|
||||
return &fr[1].cont;
|
||||
}
|
||||
|
||||
static struct closure* frame_closure_arg(frame_ptr fr, int closure) {
|
||||
assert(closure >= 0);
|
||||
assert(closure < frame_self(fr)->bc->nclosures);
|
||||
return &fr[2+closure].closure;
|
||||
}
|
||||
|
||||
static jv* frame_local_var(frame_ptr fr, int var) {
|
||||
assert(var >= 0);
|
||||
assert(var < frame_self(fr)->bc->nlocals);
|
||||
return &fr[2 + frame_self(fr)->bc->nclosures + var].jsonval;
|
||||
}
|
||||
|
||||
|
||||
static frame_ptr frame_current(struct forkable_stack* stk) {
|
||||
frame_ptr fp = forkable_stack_peek(stk);
|
||||
if (forkable_stack_peek_next(stk, fp)) {
|
||||
struct bytecode* bc = frame_self(forkable_stack_peek_next(stk, fp))->bc;
|
||||
assert(frame_self(fp)->retaddr >= bc->code && frame_self(fp)->retaddr < bc->code + bc->codelen);
|
||||
} else {
|
||||
assert(frame_self(fp)->retaddr == 0);
|
||||
}
|
||||
return fp;
|
||||
}
|
||||
|
||||
static struct bytecode* frame_current_bytecode(struct forkable_stack* stk) {
|
||||
return frame_self(frame_current(stk))->bc;
|
||||
}
|
||||
static uint16_t** frame_current_retaddr(struct forkable_stack* stk) {
|
||||
return &frame_self(frame_current(stk))->retaddr;
|
||||
}
|
||||
|
||||
static frame_ptr frame_get_parent(struct forkable_stack* stk, frame_ptr fr) {
|
||||
return forkable_stack_from_idx(stk, frame_self(fr)->env);
|
||||
}
|
||||
|
||||
static frame_ptr frame_get_level(struct forkable_stack* stk, frame_ptr fr, int level) {
|
||||
for (int i=0; i<level; i++) {
|
||||
fr = frame_get_parent(stk, fr);
|
||||
}
|
||||
return fr;
|
||||
}
|
||||
|
||||
static frame_ptr frame_push(struct forkable_stack* stk, struct closure cl, uint16_t* retaddr) {
|
||||
frame_ptr fp = forkable_stack_push(stk, frame_size(cl.bc));
|
||||
struct continuation* cc = frame_self(fp);
|
||||
cc->bc = cl.bc;
|
||||
cc->env = cl.env;
|
||||
cc->retaddr = retaddr;
|
||||
for (int i=0; i<cl.bc->nlocals; i++) {
|
||||
*frame_local_var(fp, i) = jv_invalid();
|
||||
}
|
||||
return fp;
|
||||
}
|
||||
|
||||
static void frame_pop(struct forkable_stack* stk) {
|
||||
frame_ptr fp = frame_current(stk);
|
||||
if (forkable_stack_pop_will_free(stk)) {
|
||||
int nlocals = frame_self(fp)->bc->nlocals;
|
||||
for (int i=0; i<nlocals; i++) {
|
||||
jv_free(*frame_local_var(fp, i));
|
||||
}
|
||||
}
|
||||
forkable_stack_pop(stk);
|
||||
}
|
||||
|
||||
#endif
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
|
||||
#define _GNU_SOURCE /* for RTLD_NEXT */
|
||||
#include <assert.h>
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static FILE *fail;
|
||||
static FILE *fail_read;
|
||||
static FILE *fail_write;
|
||||
static FILE *fail_close;
|
||||
static int error;
|
||||
|
||||
static FILE * (*real_fopen)(const char *, const char *);
|
||||
static int (*real_fclose)(FILE *);
|
||||
static int (*real_ferror)(FILE *);
|
||||
static void (*real_clearerr)(FILE *);
|
||||
static char * (*real_fgets)(char *, int, FILE *);
|
||||
static size_t (*real_fread)(void *, size_t, size_t, FILE *);
|
||||
static size_t (*real_fwrite)(const void *, size_t, size_t, FILE *);
|
||||
|
||||
#define GET_REAL(sym) \
|
||||
do { \
|
||||
if (real_ ## sym == 0) { \
|
||||
real_ ## sym = dlsym(RTLD_NEXT, #sym); \
|
||||
assert(real_ ## sym != 0); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define dbg_write(msg) (void)write(2, msg, sizeof(msg) - 1)
|
||||
|
||||
#define dbg() \
|
||||
do { \
|
||||
dbg_write("here: "); \
|
||||
dbg_write(__func__); \
|
||||
dbg_write("!\n"); \
|
||||
} while (0)
|
||||
|
||||
FILE *fopen(const char *path, const char *mode) {
|
||||
GET_REAL(fopen);
|
||||
fail = fail_read = fail_write = fail_close = 0;
|
||||
FILE *f = real_fopen(path, mode);
|
||||
error = EIO;
|
||||
if (strcmp(path, "fail_read") == 0) {
|
||||
fail = fail_read = f;
|
||||
} else if (strncmp(path, "fail_write", sizeof("fail_write") - 1) == 0) {
|
||||
// Not that jq opens files for write anyways...
|
||||
fail = fail_write = f;
|
||||
if (strcmp(path, "fail_write_enospc") == 0)
|
||||
error = ENOSPC;
|
||||
} else if (strncmp(path, "fail_close", sizeof("fail_close") - 1) == 0) {
|
||||
fail = fail_close = f;
|
||||
if (strcmp(path, "fail_close_enospc") == 0)
|
||||
error = ENOSPC;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
int fclose(FILE *f) {
|
||||
GET_REAL(fclose);
|
||||
int res = real_fclose(f);
|
||||
if (fail_close == f) {
|
||||
fail = fail_read = fail_write = fail_close = 0;
|
||||
return EOF;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
char * fgets(char *buf, int len, FILE *f) {
|
||||
GET_REAL(fgets);
|
||||
char *res = real_fgets(buf, len, f);
|
||||
if (fail_read == f)
|
||||
return 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
size_t fread(void *buf, size_t sz, size_t nemb, FILE *f) {
|
||||
GET_REAL(fread);
|
||||
size_t res = real_fread(buf, sz, nemb, f);
|
||||
if (fail_read == f)
|
||||
return 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
size_t fwrite(const void *buf, size_t sz, size_t nemb, FILE *f) {
|
||||
GET_REAL(fwrite);
|
||||
size_t res = real_fwrite(buf, sz, nemb, f);
|
||||
if (fail_write == f)
|
||||
return 0;
|
||||
return res;
|
||||
}
|
||||
|
||||
int ferror(FILE *f) {
|
||||
GET_REAL(ferror);
|
||||
int res = real_ferror(f);
|
||||
if (fail == f) {
|
||||
errno = error;
|
||||
return 1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void clearerr(FILE *f) {
|
||||
GET_REAL(clearerr);
|
||||
real_clearerr(f);
|
||||
if (fail == f) {
|
||||
fail = fail_read = fail_write = fail_close = 0;
|
||||
error = 0;
|
||||
}
|
||||
}
|
||||
+3075
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,60 @@
|
||||
#ifndef JQ_H
|
||||
#define JQ_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <jv.h>
|
||||
|
||||
enum {JQ_DEBUG_TRACE = 1};
|
||||
|
||||
typedef struct jq_state jq_state;
|
||||
typedef void (*jq_msg_cb)(void *, jv);
|
||||
|
||||
jq_state *jq_init(void);
|
||||
void jq_set_error_cb(jq_state *, jq_msg_cb, void *);
|
||||
void jq_get_error_cb(jq_state *, jq_msg_cb *, void **);
|
||||
void jq_set_nomem_handler(jq_state *, void (*)(void *), void *);
|
||||
jv jq_format_error(jv msg);
|
||||
void jq_report_error(jq_state *, jv);
|
||||
int jq_compile(jq_state *, const char*);
|
||||
int jq_compile_args(jq_state *, const char*, jv);
|
||||
void jq_dump_disassembly(jq_state *, int);
|
||||
void jq_start(jq_state *, jv value, int);
|
||||
jv jq_next(jq_state *);
|
||||
void jq_teardown(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 **);
|
||||
|
||||
void jq_set_debug_cb(jq_state *, jq_msg_cb, void *);
|
||||
void jq_get_debug_cb(jq_state *, jq_msg_cb *, void **);
|
||||
|
||||
void jq_set_attrs(jq_state *, jv);
|
||||
jv jq_get_attrs(jq_state *);
|
||||
jv jq_get_jq_origin(jq_state *);
|
||||
jv jq_get_prog_origin(jq_state *);
|
||||
jv jq_get_lib_dirs(jq_state *);
|
||||
void jq_set_attr(jq_state *, jv, jv);
|
||||
jv jq_get_attr(jq_state *, jv);
|
||||
|
||||
/*
|
||||
* We use char * instead of jf for filenames here because filenames
|
||||
* should be in the process' locale's codeset, which may not be UTF-8,
|
||||
* whereas jv string values must be in UTF-8. This way the caller
|
||||
* doesn't have to perform any codeset conversions.
|
||||
*/
|
||||
typedef struct jq_util_input_state jq_util_input_state;
|
||||
typedef void (*jq_util_msg_cb)(void *, const char *);
|
||||
|
||||
jq_util_input_state *jq_util_input_init(jq_util_msg_cb, void *);
|
||||
void jq_util_input_set_parser(jq_util_input_state *, jv_parser *, int);
|
||||
void jq_util_input_free(jq_util_input_state **);
|
||||
void jq_util_input_add_input(jq_util_input_state *, const char *);
|
||||
int jq_util_input_errors(jq_util_input_state *);
|
||||
jv jq_util_input_next_input(jq_util_input_state *);
|
||||
jv jq_util_input_next_input_cb(jq_state *, void *);
|
||||
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*);
|
||||
|
||||
#endif /* !JQ_H */
|
||||
@@ -5,7 +5,7 @@ Name: jq
|
||||
Version: %{myver}
|
||||
Release: %{myrel}%{?dist}
|
||||
Source0: jq-%{myver}.tar.gz
|
||||
URL: https://github.com/stedolan/jq
|
||||
URL: http://stedolan.github.io/jq
|
||||
License: BSD
|
||||
AutoReqProv: no
|
||||
#BuildPrereq: autoconf, libtool, automake, flex, bison, python
|
||||
@@ -52,11 +52,16 @@ rm -rf %{buildroot}
|
||||
%endif
|
||||
%{_datadir}/doc/%{name}/AUTHORS
|
||||
%{_datadir}/doc/%{name}/COPYING
|
||||
%{_datadir}/doc/%{name}/INSTALL
|
||||
%{_datadir}/doc/%{name}/NEWS
|
||||
%{_datadir}/doc/%{name}/README
|
||||
%{_datadir}/doc/%{name}/README.md
|
||||
%{_datadir}/man/man1/jq.1
|
||||
%{_includedir}/jq.h
|
||||
%{_includedir}/jv.h
|
||||
%{_prefix}/lib/libjq.a
|
||||
%{_prefix}/lib/libjq.la
|
||||
%{_prefix}/lib/libjq.so
|
||||
%{_prefix}/lib/libjq.so.1
|
||||
%{_prefix}/lib/libjq.so.1.0.4
|
||||
|
||||
%changelog
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#ifndef JQ_PARSER_H
|
||||
#define JQ_PARSER_H
|
||||
#include "locfile.h"
|
||||
#include "compile.h"
|
||||
|
||||
int jq_parse(struct locfile* source, block* answer);
|
||||
int jq_parse_library(struct locfile* locations, block* answer);
|
||||
|
||||
@@ -3,15 +3,23 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "jv.h"
|
||||
#include "execute.h"
|
||||
#include "jq.h"
|
||||
|
||||
static void jv_test();
|
||||
static void run_jq_tests();
|
||||
static void run_jq_tests(jv, int, FILE *);
|
||||
|
||||
|
||||
int jq_testsuite(int argc, char* argv[]) {
|
||||
int jq_testsuite(jv libdirs, int verbose, int argc, char* argv[]) {
|
||||
FILE *testdata = stdin;
|
||||
jv_test();
|
||||
run_jq_tests(stdin);
|
||||
if (argc > 0) {
|
||||
testdata = fopen(argv[0], "r");
|
||||
if (!testdata) {
|
||||
perror("fopen");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
run_jq_tests(libdirs, verbose, testdata);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -22,36 +30,123 @@ static int skipline(const char* buf) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void run_jq_tests(FILE *testdata) {
|
||||
static int checkerrormsg(const char* buf) {
|
||||
return strcmp(buf, "%%FAIL\n") == 0;
|
||||
}
|
||||
|
||||
static int checkfail(const char* buf) {
|
||||
return strcmp(buf, "%%FAIL\n") == 0 || strcmp(buf, "%%FAIL IGNORE MSG\n") == 0;
|
||||
}
|
||||
|
||||
struct err_data {
|
||||
char buf[4096];
|
||||
};
|
||||
|
||||
static void test_err_cb(void *data, jv e) {
|
||||
struct err_data *err_data = data;
|
||||
if (jv_get_kind(e) != JV_KIND_STRING)
|
||||
e = jv_dump_string(e, JV_PRINT_INVALID);
|
||||
if (!strncmp(jv_string_value(e), "jq: error", sizeof("jq: error") - 1))
|
||||
snprintf(err_data->buf, sizeof(err_data->buf), "%s", jv_string_value(e));
|
||||
if (strchr(err_data->buf, '\n'))
|
||||
*(strchr(err_data->buf, '\n')) = '\0';
|
||||
jv_free(e);
|
||||
}
|
||||
|
||||
static void run_jq_tests(jv lib_dirs, int verbose, FILE *testdata) {
|
||||
char prog[4096];
|
||||
char buf[4096];
|
||||
struct err_data err_msg;
|
||||
int tests = 0, passed = 0, invalid = 0;
|
||||
unsigned int lineno = 0;
|
||||
int must_fail = 0;
|
||||
int check_msg = 0;
|
||||
jq_state *jq = NULL;
|
||||
|
||||
jq = jq_init();
|
||||
assert(jq);
|
||||
if (jv_get_kind(lib_dirs) == JV_KIND_NULL)
|
||||
lib_dirs = jv_array();
|
||||
jq_set_attr(jq, jv_string("JQ_LIBRARY_PATH"), lib_dirs);
|
||||
|
||||
while (1) {
|
||||
if (!fgets(buf, sizeof(buf), testdata)) break;
|
||||
if (skipline(buf)) continue;
|
||||
if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = 0;
|
||||
printf("Testing %s\n", buf);
|
||||
if (!fgets(prog, sizeof(prog), testdata)) break;
|
||||
lineno++;
|
||||
if (skipline(prog)) continue;
|
||||
if (checkfail(prog)) {
|
||||
must_fail = 1;
|
||||
check_msg = checkerrormsg(prog);
|
||||
jq_set_error_cb(jq, test_err_cb, &err_msg);
|
||||
continue;
|
||||
}
|
||||
if (prog[strlen(prog)-1] == '\n') prog[strlen(prog)-1] = 0;
|
||||
printf("Testing '%s' at line number %u\n", prog, lineno);
|
||||
int pass = 1;
|
||||
tests++;
|
||||
struct bytecode* bc = jq_compile(buf);
|
||||
if (!bc) {invalid++; continue;}
|
||||
printf("Disassembly:\n");
|
||||
dump_disassembly(2, bc);
|
||||
printf("\n");
|
||||
fgets(buf, sizeof(buf), testdata);
|
||||
int compiled = jq_compile(jq, prog);
|
||||
|
||||
if (must_fail) {
|
||||
jq_set_error_cb(jq, NULL, NULL);
|
||||
if (!fgets(buf, sizeof(buf), testdata)) { invalid++; break; }
|
||||
lineno++;
|
||||
if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = 0;
|
||||
if (compiled) {
|
||||
printf("*** Test program compiled that should not have at line %u: %s\n", lineno, prog);
|
||||
must_fail = 0;
|
||||
check_msg = 0;
|
||||
invalid++;
|
||||
continue;
|
||||
}
|
||||
if (check_msg && strcmp(buf, err_msg.buf) != 0) {
|
||||
printf("*** Erroneous test program failed with wrong message (%s) at line %u: %s\n", err_msg.buf, lineno, prog);
|
||||
invalid++;
|
||||
} else {
|
||||
passed++;
|
||||
}
|
||||
must_fail = 0;
|
||||
check_msg = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!compiled) {
|
||||
printf("*** Test program failed to compile at line %u: %s\n", lineno, prog);
|
||||
invalid++;
|
||||
// skip past test data
|
||||
while (fgets(buf, sizeof(buf), testdata)) {
|
||||
lineno++;
|
||||
if (buf[0] == '\n' || (buf[0] == '\r' && buf[1] == '\n'))
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (verbose) {
|
||||
printf("Disassembly:\n");
|
||||
jq_dump_disassembly(jq, 2);
|
||||
printf("\n");
|
||||
}
|
||||
if (!fgets(buf, sizeof(buf), testdata)) { invalid++; break; }
|
||||
lineno++;
|
||||
jv input = jv_parse(buf);
|
||||
if (!jv_is_valid(input)){ invalid++; continue; }
|
||||
jq_init(bc, input, &jq, JQ_DEBUG_TRACE);
|
||||
if (!jv_is_valid(input)) {
|
||||
printf("*** Input is invalid on line %u: %s\n", lineno, buf);
|
||||
invalid++;
|
||||
continue;
|
||||
}
|
||||
jq_start(jq, input, verbose ? JQ_DEBUG_TRACE : 0);
|
||||
|
||||
while (fgets(buf, sizeof(buf), testdata)) {
|
||||
lineno++;
|
||||
if (skipline(buf)) break;
|
||||
jv expected = jv_parse(buf);
|
||||
if (!jv_is_valid(expected)){ invalid++; continue; }
|
||||
if (!jv_is_valid(expected)) {
|
||||
printf("*** Expected result is invalid on line %u: %s\n", lineno, buf);
|
||||
invalid++;
|
||||
continue;
|
||||
}
|
||||
jv actual = jq_next(jq);
|
||||
if (!jv_is_valid(actual)) {
|
||||
jv_free(actual);
|
||||
printf("*** Insufficient results\n");
|
||||
printf("*** Insufficient results for test at line number %u: %s\n", lineno, prog);
|
||||
pass = 0;
|
||||
break;
|
||||
} else if (!jv_equal(jv_copy(expected), jv_copy(actual))) {
|
||||
@@ -59,10 +154,10 @@ static void run_jq_tests(FILE *testdata) {
|
||||
jv_dump(jv_copy(expected), 0);
|
||||
printf(", but got ");
|
||||
jv_dump(jv_copy(actual), 0);
|
||||
printf("\n");
|
||||
printf(" for test at line number %u: %s\n", lineno, prog);
|
||||
pass = 0;
|
||||
}
|
||||
jv as_string = jv_dump_string(jv_copy(expected), rand() & ~JV_PRINT_COLOUR);
|
||||
jv as_string = jv_dump_string(jv_copy(expected), rand() & ~(JV_PRINT_COLOUR|JV_PRINT_REFCOUNT));
|
||||
jv reparsed = jv_parse_sized(jv_string_value(as_string), jv_string_length_bytes(jv_copy(as_string)));
|
||||
assert(jv_equal(jv_copy(expected), jv_copy(reparsed)));
|
||||
jv_free(as_string);
|
||||
@@ -75,29 +170,36 @@ static void run_jq_tests(FILE *testdata) {
|
||||
if (jv_is_valid(extra)) {
|
||||
printf("*** Superfluous result: ");
|
||||
jv_dump(extra, 0);
|
||||
printf("\n");
|
||||
printf(" for test at line number %u, %s\n", lineno, prog);
|
||||
pass = 0;
|
||||
} else {
|
||||
jv_free(extra);
|
||||
}
|
||||
}
|
||||
jq_teardown(&jq);
|
||||
bytecode_free(bc);
|
||||
passed+=pass;
|
||||
}
|
||||
jq_teardown(&jq);
|
||||
printf("%d of %d tests passed (%d malformed)\n", passed,tests,invalid);
|
||||
if (passed != tests) exit(1);
|
||||
}
|
||||
|
||||
|
||||
static void jv_test() {
|
||||
/// JSON parser regression tests
|
||||
{
|
||||
jv v = jv_parse("{\"a':\"12\"}");
|
||||
assert(jv_get_kind(v) == JV_KIND_INVALID);
|
||||
v = jv_invalid_get_msg(v);
|
||||
assert(strcmp(jv_string_value(v), "Expected separator between values at line 1, column 9 (while parsing '{\"a':\"12\"}')") == 0);
|
||||
jv_free(v);
|
||||
}
|
||||
/// Arrays and numbers
|
||||
{
|
||||
jv a = jv_array();
|
||||
assert(jv_get_kind(a) == JV_KIND_ARRAY);
|
||||
assert(jv_array_length(jv_copy(a)) == 0);
|
||||
assert(jv_array_length(jv_copy(a)) == 0);
|
||||
|
||||
|
||||
a = jv_array_append(a, jv_number(42));
|
||||
assert(jv_array_length(jv_copy(a)) == 1);
|
||||
assert(jv_number_value(jv_array_get(jv_copy(a), 0)) == 42);
|
||||
@@ -115,9 +217,9 @@ static void jv_test() {
|
||||
jv_free(a2);
|
||||
|
||||
|
||||
assert(a.val.nontrivial.ptr->count == 1);
|
||||
assert(jv_get_refcnt(a) == 1);
|
||||
a = jv_array_append(a, jv_copy(a));
|
||||
assert(a.val.nontrivial.ptr->count == 1);
|
||||
assert(jv_get_refcnt(a) == 1);
|
||||
|
||||
assert(jv_array_length(jv_copy(a)) == 2);
|
||||
assert(jv_number_value(jv_array_get(jv_copy(a), 0)) == 42);
|
||||
@@ -150,9 +252,9 @@ static void jv_test() {
|
||||
|
||||
jv_free(subarray);
|
||||
|
||||
void* before = sub2.val.nontrivial.ptr;
|
||||
void* before = sub2.u.ptr;
|
||||
sub2 = jv_array_append(sub2, jv_number(200));
|
||||
void* after = sub2.val.nontrivial.ptr;
|
||||
void* after = sub2.u.ptr;
|
||||
assert(before == after);
|
||||
jv_free(sub2);
|
||||
|
||||
@@ -172,8 +274,10 @@ static void jv_test() {
|
||||
a4 = jv_array_slice(a4, 0, 1);
|
||||
assert(jv_array_length(jv_copy(a4)) == 1);
|
||||
a4 = jv_array_append(a4, jv_number(4));
|
||||
assert(jv_array_length(a4) == 2);
|
||||
assert(jv_array_length(a5) == 2);
|
||||
assert(jv_array_length(jv_copy(a4)) == 2);
|
||||
assert(jv_array_length(jv_copy(a5)) == 2);
|
||||
jv_free(a4);
|
||||
jv_free(a5);
|
||||
|
||||
|
||||
assert(jv_array_length(jv_copy(a)) == 2);
|
||||
@@ -181,7 +285,7 @@ static void jv_test() {
|
||||
assert(jv_array_length(jv_array_get(jv_copy(a), 1)) == 1);
|
||||
|
||||
|
||||
jv_dump(jv_copy(a), 0); printf("\n");
|
||||
//jv_dump(jv_copy(a), 0); printf("\n");
|
||||
jv_free(a);
|
||||
}
|
||||
|
||||
@@ -191,16 +295,18 @@ static void jv_test() {
|
||||
assert(jv_equal(jv_string("foo"), jv_string_sized("foo", 3)));
|
||||
char nasty[] = "foo\0";
|
||||
jv shortstr = jv_string(nasty), longstr = jv_string_sized(nasty, sizeof(nasty));
|
||||
assert(jv_string_length_bytes(shortstr) == (int)strlen(nasty));
|
||||
assert(jv_string_length_bytes(longstr) == (int)sizeof(nasty));
|
||||
assert(jv_string_length_bytes(jv_copy(shortstr)) == (int)strlen(nasty));
|
||||
assert(jv_string_length_bytes(jv_copy(longstr)) == (int)sizeof(nasty));
|
||||
jv_free(shortstr);
|
||||
jv_free(longstr);
|
||||
|
||||
|
||||
|
||||
char a1s[] = "hello", a2s[] = "hello", bs[] = "goodbye";
|
||||
jv a1 = jv_string(a1s), a2 = jv_string(a2s), b = jv_string(bs);
|
||||
assert(jv_equal(jv_copy(a1), jv_copy(a2)));
|
||||
assert(jv_equal(jv_copy(a2), jv_copy(a1)));
|
||||
assert(!jv_equal(jv_copy(a1), jv_copy(b)));
|
||||
|
||||
|
||||
assert(jv_string_hash(jv_copy(a1)) == jv_string_hash(jv_copy(a1)));
|
||||
assert(jv_string_hash(jv_copy(a1)) == jv_string_hash(jv_copy(a2)));
|
||||
assert(jv_string_hash(jv_copy(b)) != jv_string_hash(jv_copy(a1)));
|
||||
@@ -234,8 +340,7 @@ static void jv_test() {
|
||||
jv_free(o1);
|
||||
assert(jv_number_value(jv_object_get(jv_copy(o2), jv_string("bar"))) == 240);
|
||||
|
||||
jv_dump(jv_copy(o2), 0); printf("\n");
|
||||
|
||||
//jv_dump(jv_copy(o2), 0); printf("\n");
|
||||
jv_free(o2);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
#ifndef JV_H
|
||||
#define JV_H
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
typedef enum {
|
||||
JV_KIND_INVALID,
|
||||
@@ -18,21 +16,19 @@ typedef enum {
|
||||
JV_KIND_OBJECT
|
||||
} jv_kind;
|
||||
|
||||
typedef struct {
|
||||
size_t count;
|
||||
} jv_refcnt;
|
||||
|
||||
typedef struct{
|
||||
jv_refcnt* ptr;
|
||||
int i[2];
|
||||
} jv_nontrivial;
|
||||
struct jv_refcnt;
|
||||
|
||||
/* All of the fields of this struct are private.
|
||||
Really. Do not play with them. */
|
||||
typedef struct {
|
||||
jv_kind kind;
|
||||
unsigned char kind_flags;
|
||||
unsigned char pad_;
|
||||
unsigned short offset; /* array offsets */
|
||||
int size;
|
||||
union {
|
||||
struct jv_refcnt* ptr;
|
||||
double number;
|
||||
jv_nontrivial nontrivial;
|
||||
} val;
|
||||
} u;
|
||||
} jv;
|
||||
|
||||
/*
|
||||
@@ -47,24 +43,28 @@ static int jv_is_valid(jv x) { return jv_get_kind(x) != JV_KIND_INVALID; }
|
||||
jv jv_copy(jv);
|
||||
void jv_free(jv);
|
||||
|
||||
int jv_get_refcnt(jv);
|
||||
|
||||
int jv_equal(jv, jv);
|
||||
int jv_identical(jv, jv);
|
||||
int jv_contains(jv, jv);
|
||||
|
||||
jv jv_invalid();
|
||||
jv jv_invalid(void);
|
||||
jv jv_invalid_with_msg(jv);
|
||||
jv jv_invalid_get_msg(jv);
|
||||
int jv_invalid_has_msg(jv);
|
||||
|
||||
|
||||
jv jv_null();
|
||||
jv jv_true();
|
||||
jv jv_false();
|
||||
jv jv_null(void);
|
||||
jv jv_true(void);
|
||||
jv jv_false(void);
|
||||
jv jv_bool(int);
|
||||
|
||||
jv jv_number(double);
|
||||
double jv_number_value(jv);
|
||||
int jv_is_integer(jv);
|
||||
|
||||
jv jv_array();
|
||||
jv jv_array(void);
|
||||
jv jv_array_sized(int);
|
||||
int jv_array_length(jv);
|
||||
jv jv_array_get(jv, int);
|
||||
@@ -72,6 +72,7 @@ jv jv_array_set(jv, int, jv);
|
||||
jv jv_array_append(jv, jv);
|
||||
jv jv_array_concat(jv, jv);
|
||||
jv jv_array_slice(jv, int, int);
|
||||
jv jv_array_indexes(jv, jv);
|
||||
#define jv_array_foreach(a, i, x) \
|
||||
for (int jv_len__ = jv_array_length(jv_copy(a)), i=0, jv_j__ = 1; \
|
||||
jv_j__; jv_j__ = 0) \
|
||||
@@ -80,23 +81,53 @@ jv jv_array_slice(jv, int, int);
|
||||
(x = jv_array_get(jv_copy(a), i), 1) : 0; \
|
||||
i++)
|
||||
|
||||
#define JV_ARRAY_1(e) (jv_array_append(jv_array(),e))
|
||||
#define JV_ARRAY_2(e1,e2) (jv_array_append(JV_ARRAY_1(e1),e2))
|
||||
#define JV_ARRAY_3(e1,e2,e3) (jv_array_append(JV_ARRAY_2(e1,e2),e3))
|
||||
#define JV_ARRAY_4(e1,e2,e3,e4) (jv_array_append(JV_ARRAY_3(e1,e2,e3),e4))
|
||||
#define JV_ARRAY_5(e1,e2,e3,e4,e5) (jv_array_append(JV_ARRAY_4(e1,e2,e3,e4),e5))
|
||||
#define JV_ARRAY_6(e1,e2,e3,e4,e5,e6) (jv_array_append(JV_ARRAY_5(e1,e2,e3,e4,e5),e6))
|
||||
#define JV_ARRAY_7(e1,e2,e3,e4,e5,e6,e7) (jv_array_append(JV_ARRAY_6(e1,e2,e3,e4,e5,e6),e7))
|
||||
#define JV_ARRAY_8(e1,e2,e3,e4,e5,e6,e7,e8) (jv_array_append(JV_ARRAY_7(e1,e2,e3,e4,e5,e6,e7),e8))
|
||||
#define JV_ARRAY_9(e1,e2,e3,e4,e5,e6,e7,e8,e9) (jv_array_append(JV_ARRAY_8(e1,e2,e3,e4,e5,e6,e7,e8),e9))
|
||||
#define JV_ARRAY_IDX(_1,_2,_3,_4,_5,_6,_7,_8,_9,NAME,...) NAME
|
||||
#define JV_ARRAY(...) \
|
||||
JV_ARRAY_IDX(__VA_ARGS__, JV_ARRAY_9, JV_ARRAY_8, JV_ARRAY_7, JV_ARRAY_6, JV_ARRAY_5, JV_ARRAY_4, JV_ARRAY_3, JV_ARRAY_2, JV_ARRAY_1)(__VA_ARGS__)
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define JV_PRINTF_LIKE(fmt_arg_num, args_num) \
|
||||
__attribute__ ((__format__( __printf__, fmt_arg_num, args_num)))
|
||||
#define JV_VPRINTF_LIKE(fmt_arg_num) \
|
||||
__attribute__ ((__format__( __printf__, fmt_arg_num, 0)))
|
||||
#endif
|
||||
|
||||
|
||||
jv jv_string(const char*);
|
||||
jv jv_string_sized(const char*, int);
|
||||
jv jv_string_empty(int len);
|
||||
int jv_string_length_bytes(jv);
|
||||
int jv_string_length_codepoints(jv);
|
||||
uint32_t jv_string_hash(jv);
|
||||
unsigned long jv_string_hash(jv);
|
||||
const char* jv_string_value(jv);
|
||||
jv jv_string_indexes(jv j, jv k);
|
||||
jv jv_string_slice(jv j, int start, int end);
|
||||
jv jv_string_concat(jv, jv);
|
||||
jv jv_string_fmt(const char*, ...);
|
||||
jv jv_string_vfmt(const char*, va_list) JV_VPRINTF_LIKE(1);
|
||||
jv jv_string_fmt(const char*, ...) JV_PRINTF_LIKE(1, 2);
|
||||
jv jv_string_append_codepoint(jv a, uint32_t c);
|
||||
jv jv_string_append_buf(jv a, const char* buf, int len);
|
||||
jv jv_string_append_str(jv a, const char* str);
|
||||
jv jv_string_split(jv j, jv sep);
|
||||
jv jv_string_explode(jv j);
|
||||
jv jv_string_implode(jv j);
|
||||
|
||||
jv jv_object();
|
||||
jv jv_object(void);
|
||||
jv jv_object_get(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);
|
||||
jv jv_object_merge(jv, jv);
|
||||
jv jv_object_merge_recursive(jv, jv);
|
||||
|
||||
int jv_object_iter(jv);
|
||||
int jv_object_iter_next(jv, int);
|
||||
@@ -112,25 +143,76 @@ jv jv_object_iter_value(jv, int);
|
||||
1) \
|
||||
: 0; \
|
||||
jv_i__ = jv_object_iter_next(t, jv_i__)) \
|
||||
|
||||
|
||||
#define JV_OBJECT_1(k) (jv_object_set(jv_object(),(k),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()))
|
||||
#define JV_OBJECT_4(k1,v1,k2,v2) (jv_object_set(JV_OBJECT_2(k1,v1),k2,v2))
|
||||
#define JV_OBJECT_5(k1,v1,k2,v2,k3) (jv_object_set(JV_OBJECT_4(k1,v1,k2,v2),k3,jv_null))
|
||||
#define JV_OBJECT_6(k1,v1,k2,v2,k3,v3) (jv_object_set(JV_OBJECT_4(k1,v1,k2,v2),k3,v3))
|
||||
#define JV_OBJECT_7(k1,v1,k2,v2,k3,v3,k4) (jv_object_set(JV_OBJECT_6(k1,v1,k2,v2,k3,v3),k4,jv_null()))
|
||||
#define JV_OBJECT_8(k1,v1,k2,v2,k3,v3,k4,v4) (jv_object_set(JV_OBJECT_6(k1,v1,k2,v2,k3,v3),k4,v4))
|
||||
#define JV_OBJECT_IDX(_1,_2,_3,_4,_5,_6,_7,_8,NAME,...) NAME
|
||||
#define JV_OBJECT(...) \
|
||||
JV_OBJECT_IDX(__VA_ARGS__, JV_OBJECT_8, JV_OBJECT_7, JV_OBJECT_6, JV_OBJECT_5, JV_OBJECT_4, JV_OBJECT_3, JV_OBJECT_2, JV_OBJECT_1)(__VA_ARGS__)
|
||||
|
||||
|
||||
|
||||
int jv_get_refcnt(jv);
|
||||
|
||||
enum { JV_PRINT_PRETTY = 1, JV_PRINT_ASCII = 2, JV_PRINT_COLOUR = 4 };
|
||||
enum jv_print_flags {
|
||||
JV_PRINT_PRETTY = 1,
|
||||
JV_PRINT_ASCII = 2,
|
||||
JV_PRINT_COLOUR = 4,
|
||||
JV_PRINT_SORTED = 8,
|
||||
JV_PRINT_INVALID = 16,
|
||||
JV_PRINT_REFCOUNT = 32,
|
||||
JV_PRINT_TAB = 64,
|
||||
JV_PRINT_ISATTY = 128,
|
||||
JV_PRINT_SPACE0 = 256,
|
||||
JV_PRINT_SPACE1 = 512,
|
||||
JV_PRINT_SPACE2 = 1024,
|
||||
};
|
||||
#define JV_PRINT_INDENT_FLAGS(n) \
|
||||
((n) < 0 || (n) > 7 ? JV_PRINT_TAB | JV_PRINT_PRETTY : (n) == 0 ? 0 : (n) << 8 | JV_PRINT_PRETTY)
|
||||
void jv_dumpf(jv, FILE *f, int flags);
|
||||
void jv_dump(jv, int flags);
|
||||
void jv_show(jv, int flags);
|
||||
jv jv_dump_string(jv, int flags);
|
||||
char *jv_dump_string_trunc(jv x, char *outbuf, size_t bufsize);
|
||||
|
||||
enum {
|
||||
JV_PARSE_SEQ = 1,
|
||||
JV_PARSE_STREAMING = 2,
|
||||
JV_PARSE_STREAM_ERRORS = 4,
|
||||
};
|
||||
|
||||
jv jv_parse(const char* string);
|
||||
jv jv_parse_sized(const char* string, int length);
|
||||
|
||||
typedef void (*jv_nomem_handler_f)(void *);
|
||||
void jv_nomem_handler(jv_nomem_handler_f, void *);
|
||||
|
||||
jv jv_load_file(const char *, int);
|
||||
|
||||
typedef struct jv_parser jv_parser;
|
||||
jv_parser* jv_parser_new(int);
|
||||
void jv_parser_set_buf(jv_parser*, const char*, int, int);
|
||||
int jv_parser_remaining(jv_parser*);
|
||||
jv jv_parser_next(jv_parser*);
|
||||
void jv_parser_free(jv_parser*);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
jv jv_get(jv, jv);
|
||||
jv jv_set(jv, jv, jv);
|
||||
jv jv_has(jv, jv);
|
||||
jv jv_setpath(jv, jv, jv);
|
||||
jv jv_getpath(jv, jv);
|
||||
jv jv_delpaths(jv, jv);
|
||||
jv jv_keys(jv /*object or array*/);
|
||||
jv jv_keys_unsorted(jv /*object or array*/);
|
||||
int jv_cmp(jv, jv);
|
||||
jv jv_group(jv, jv);
|
||||
jv jv_sort(jv, jv);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
+142
@@ -1,12 +1,123 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "jv_alloc.h"
|
||||
|
||||
struct nomem_handler {
|
||||
jv_nomem_handler_f handler;
|
||||
void *data;
|
||||
};
|
||||
|
||||
#if !defined(HAVE_PTHREAD_KEY_CREATE) || \
|
||||
!defined(HAVE_PTHREAD_ONCE) || \
|
||||
!defined(HAVE_ATEXIT)
|
||||
|
||||
/* Try thread-local storage? */
|
||||
|
||||
#ifdef _MSC_VER
|
||||
/* Visual C++: yes */
|
||||
static __declspec(thread) struct nomem_handler nomem_handler;
|
||||
#define USE_TLS
|
||||
#else
|
||||
#ifdef HAVE___THREAD
|
||||
/* GCC and friends: yes */
|
||||
static __thread struct nomem_handler nomem_handler;
|
||||
#define USE_TLS
|
||||
#endif /* HAVE___THREAD */
|
||||
#endif /* _MSC_VER */
|
||||
|
||||
#endif /* !HAVE_PTHREAD_KEY_CREATE */
|
||||
|
||||
#ifdef USE_TLS
|
||||
void jv_nomem_handler(jv_nomem_handler_f handler, void *data) {
|
||||
nomem_handler.handler = handler;
|
||||
}
|
||||
|
||||
static void memory_exhausted() {
|
||||
if (nomem_handler.handler)
|
||||
nomem_handler.handler(nomem_handler.data); // Maybe handler() will longjmp() to safety
|
||||
// Or not
|
||||
fprintf(stderr, "error: cannot allocate memory\n");
|
||||
abort();
|
||||
}
|
||||
#else /* USE_TLS */
|
||||
|
||||
#ifdef HAVE_PTHREAD_KEY_CREATE
|
||||
#include <pthread.h>
|
||||
|
||||
pthread_key_t nomem_handler_key;
|
||||
pthread_once_t mem_once = PTHREAD_ONCE_INIT;
|
||||
|
||||
static void tsd_fini(void) {
|
||||
struct nomem_handler *nomem_handler;
|
||||
nomem_handler = pthread_getspecific(nomem_handler_key);
|
||||
if (nomem_handler) {
|
||||
(void) pthread_setspecific(nomem_handler_key, NULL);
|
||||
free(nomem_handler);
|
||||
}
|
||||
}
|
||||
|
||||
static void tsd_init(void) {
|
||||
if (pthread_key_create(&nomem_handler_key, NULL) != 0) {
|
||||
fprintf(stderr, "error: cannot create thread specific key");
|
||||
abort();
|
||||
}
|
||||
if (atexit(tsd_fini) != 0) {
|
||||
fprintf(stderr, "error: cannot set an exit handler");
|
||||
abort();
|
||||
}
|
||||
struct nomem_handler *nomem_handler = calloc(1, sizeof(struct nomem_handler));
|
||||
if (pthread_setspecific(nomem_handler_key, nomem_handler) != 0) {
|
||||
fprintf(stderr, "error: cannot set thread specific data");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void jv_nomem_handler(jv_nomem_handler_f handler, void *data) {
|
||||
pthread_once(&mem_once, tsd_init); // cannot fail
|
||||
struct nomem_handler *nomem_handler;
|
||||
|
||||
nomem_handler = pthread_getspecific(nomem_handler_key);
|
||||
if (nomem_handler == NULL) {
|
||||
handler(data);
|
||||
fprintf(stderr, "error: cannot allocate memory\n");
|
||||
abort();
|
||||
}
|
||||
nomem_handler->handler = handler;
|
||||
nomem_handler->data = data;
|
||||
}
|
||||
|
||||
static void memory_exhausted() {
|
||||
struct nomem_handler *nomem_handler;
|
||||
|
||||
pthread_once(&mem_once, tsd_init);
|
||||
nomem_handler = pthread_getspecific(nomem_handler_key);
|
||||
if (nomem_handler)
|
||||
nomem_handler->handler(nomem_handler->data); // Maybe handler() will longjmp() to safety
|
||||
// Or not
|
||||
fprintf(stderr, "error: cannot allocate memory\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* No thread-local storage of any kind that we know how to handle */
|
||||
|
||||
static struct nomem_handler nomem_handler;
|
||||
void jv_nomem_handler(jv_nomem_handler_f handler, void *data) {
|
||||
nomem_handler.handler = handler;
|
||||
nomem_handler.data = data;
|
||||
}
|
||||
|
||||
static void memory_exhausted() {
|
||||
fprintf(stderr, "error: cannot allocate memory\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
#endif /* HAVE_PTHREAD_KEY_CREATE */
|
||||
#endif /* USE_TLS */
|
||||
|
||||
|
||||
void* jv_mem_alloc(size_t sz) {
|
||||
void* p = malloc(sz);
|
||||
if (!p) {
|
||||
@@ -15,6 +126,34 @@ void* jv_mem_alloc(size_t sz) {
|
||||
return p;
|
||||
}
|
||||
|
||||
void* jv_mem_alloc_unguarded(size_t sz) {
|
||||
return malloc(sz);
|
||||
}
|
||||
|
||||
void* jv_mem_calloc(size_t nemb, size_t sz) {
|
||||
void* p = calloc(nemb, sz);
|
||||
if (!p) {
|
||||
memory_exhausted();
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
void* jv_mem_calloc_unguarded(size_t nemb, size_t sz) {
|
||||
return calloc(nemb, sz);
|
||||
}
|
||||
|
||||
char* jv_mem_strdup(const char *s) {
|
||||
char *p = strdup(s);
|
||||
if (!p) {
|
||||
memory_exhausted();
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
char* jv_mem_strdup_unguarded(const char *s) {
|
||||
return strdup(s);
|
||||
}
|
||||
|
||||
void jv_mem_free(void* p) {
|
||||
free(p);
|
||||
}
|
||||
@@ -30,8 +169,11 @@ void* jv_mem_realloc(void* p, size_t sz) {
|
||||
#ifndef NDEBUG
|
||||
volatile char jv_mem_uninitialised;
|
||||
__attribute__((constructor)) void jv_mem_uninit_setup(){
|
||||
// ignore warning that this reads uninitialized memory - that's the point!
|
||||
#ifndef __clang_analyzer__
|
||||
char* p = malloc(1);
|
||||
jv_mem_uninitialised = *p;
|
||||
free(p);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
@@ -2,6 +2,7 @@
|
||||
#define JV_ALLOC_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include "jv.h"
|
||||
|
||||
#ifndef NDEBUG
|
||||
extern volatile char jv_mem_uninitialised;
|
||||
@@ -15,6 +16,11 @@ static void jv_mem_invalidate(void* mem, size_t n) {
|
||||
}
|
||||
|
||||
void* jv_mem_alloc(size_t);
|
||||
void* jv_mem_alloc_unguarded(size_t);
|
||||
void* jv_mem_calloc(size_t, size_t);
|
||||
void* jv_mem_calloc_unguarded(size_t, size_t);
|
||||
char* jv_mem_strdup(const char *);
|
||||
char* jv_mem_strdup_unguarded(const char *);
|
||||
void jv_mem_free(void*);
|
||||
__attribute__((warn_unused_result)) void* jv_mem_realloc(void*, size_t);
|
||||
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
#include "jv_aux.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include "jv_alloc.h"
|
||||
|
||||
static int parse_slice(jv array, jv slice, int* pstart, int* pend) {
|
||||
static int parse_slice(jv j, jv slice, int* pstart, int* pend) {
|
||||
// Array slices
|
||||
int len = jv_array_length(jv_copy(array));
|
||||
jv start_jv = jv_object_get(jv_copy(slice), jv_string("start"));
|
||||
jv end_jv = jv_object_get(slice, jv_string("end"));
|
||||
if (jv_get_kind(start_jv) == JV_KIND_NULL) {
|
||||
jv_free(start_jv);
|
||||
start_jv = jv_number(0);
|
||||
}
|
||||
int len;
|
||||
if (jv_get_kind(j) == JV_KIND_ARRAY)
|
||||
len = jv_array_length(jv_copy(j));
|
||||
else
|
||||
len = jv_string_length_codepoints(jv_copy(j));
|
||||
if (jv_get_kind(end_jv) == JV_KIND_NULL) {
|
||||
jv_free(end_jv);
|
||||
end_jv = jv_number(len);
|
||||
@@ -22,13 +26,18 @@ static int parse_slice(jv array, jv slice, int* pstart, int* pend) {
|
||||
jv_free(end_jv);
|
||||
return 0;
|
||||
} else {
|
||||
int start = (int)jv_number_value(start_jv);
|
||||
int end = (int)jv_number_value(end_jv);
|
||||
if (start < 0) start = len + start;
|
||||
if (end < 0) end = len + end;
|
||||
double dstart = jv_number_value(start_jv);
|
||||
double dend = jv_number_value(end_jv);
|
||||
if (dstart < 0) dstart += len;
|
||||
if (dend < 0) dend += len;
|
||||
if (dstart < 0) dstart = 0;
|
||||
if (dstart > len) dstart = len;
|
||||
|
||||
int start = (int)dstart;
|
||||
int end = (dend > len) ? len : (int)dend;
|
||||
// Ends are exclusive but e.g. 1 < 1.5 so :1.5 should be :2 not :1
|
||||
if(end < dend) end += 1;
|
||||
|
||||
if (start < 0) start = 0;
|
||||
if (start > len) start = len;
|
||||
if (end > len) end = len;
|
||||
if (end < start) end = start;
|
||||
assert(0 <= start && start <= end && end <= len);
|
||||
@@ -47,10 +56,18 @@ jv jv_get(jv t, jv k) {
|
||||
v = jv_null();
|
||||
}
|
||||
} else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_NUMBER) {
|
||||
// FIXME: don't do lookup for noninteger index
|
||||
v = jv_array_get(t, (int)jv_number_value(k));
|
||||
if (!jv_is_valid(v)) {
|
||||
jv_free(v);
|
||||
if(jv_is_integer(k)){
|
||||
int idx = (int)jv_number_value(k);
|
||||
if (idx < 0)
|
||||
idx += jv_array_length(jv_copy(t));
|
||||
v = jv_array_get(t, idx);
|
||||
if (!jv_is_valid(v)) {
|
||||
jv_free(v);
|
||||
v = jv_null();
|
||||
}
|
||||
} else {
|
||||
jv_free(t);
|
||||
jv_free(k);
|
||||
v = jv_null();
|
||||
}
|
||||
} else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_OBJECT) {
|
||||
@@ -61,17 +78,38 @@ jv jv_get(jv t, jv k) {
|
||||
v = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an array slice must be numbers"));
|
||||
jv_free(t);
|
||||
}
|
||||
} else if (jv_get_kind(t) == JV_KIND_NULL &&
|
||||
(jv_get_kind(k) == JV_KIND_STRING ||
|
||||
jv_get_kind(k) == JV_KIND_NUMBER ||
|
||||
} else if (jv_get_kind(t) == JV_KIND_STRING && jv_get_kind(k) == JV_KIND_OBJECT) {
|
||||
int start, end;
|
||||
if (parse_slice(t, k, &start, &end)) {
|
||||
v = jv_string_slice(t, start, end);
|
||||
} else {
|
||||
v = jv_invalid_with_msg(jv_string_fmt("Start and end indices of an string slice must be numbers"));
|
||||
jv_free(t);
|
||||
}
|
||||
} else if (jv_get_kind(t) == JV_KIND_ARRAY && jv_get_kind(k) == JV_KIND_ARRAY) {
|
||||
v = jv_array_indexes(t, k);
|
||||
} else if (jv_get_kind(t) == JV_KIND_NULL &&
|
||||
(jv_get_kind(k) == JV_KIND_STRING ||
|
||||
jv_get_kind(k) == JV_KIND_NUMBER ||
|
||||
jv_get_kind(k) == JV_KIND_OBJECT)) {
|
||||
jv_free(t);
|
||||
jv_free(k);
|
||||
v = jv_null();
|
||||
} else {
|
||||
v = jv_invalid_with_msg(jv_string_fmt("Cannot index %s with %s",
|
||||
jv_kind_name(jv_get_kind(t)),
|
||||
jv_kind_name(jv_get_kind(k))));
|
||||
/*
|
||||
* If k is a short string it's probably from a jq .foo expression or
|
||||
* similar, in which case putting it in the invalid msg may help the
|
||||
* user. The length 30 is arbitrary.
|
||||
*/
|
||||
if (jv_get_kind(k) == JV_KIND_STRING && jv_string_length_bytes(jv_copy(k)) < 30) {
|
||||
v = jv_invalid_with_msg(jv_string_fmt("Cannot index %s with string \"%s\"",
|
||||
jv_kind_name(jv_get_kind(t)),
|
||||
jv_string_value(k)));
|
||||
} else {
|
||||
v = jv_invalid_with_msg(jv_string_fmt("Cannot index %s with %s",
|
||||
jv_kind_name(jv_get_kind(t)),
|
||||
jv_kind_name(jv_get_kind(k))));
|
||||
}
|
||||
jv_free(t);
|
||||
jv_free(k);
|
||||
}
|
||||
@@ -85,7 +123,7 @@ jv jv_set(jv t, jv k, jv v) {
|
||||
return v;
|
||||
}
|
||||
int isnull = jv_get_kind(t) == JV_KIND_NULL;
|
||||
if (jv_get_kind(k) == JV_KIND_STRING &&
|
||||
if (jv_get_kind(k) == JV_KIND_STRING &&
|
||||
(jv_get_kind(t) == JV_KIND_OBJECT || isnull)) {
|
||||
if (isnull) t = jv_object();
|
||||
t = jv_object_set(t, k, v);
|
||||
@@ -176,7 +214,7 @@ jv jv_has(jv t, jv k) {
|
||||
jv jv_dels(jv t, jv keys) {
|
||||
assert(jv_get_kind(keys) == JV_KIND_ARRAY);
|
||||
assert(jv_is_valid(t));
|
||||
|
||||
|
||||
if (jv_get_kind(t) == JV_KIND_NULL || jv_array_length(jv_copy(keys)) == 0) {
|
||||
// no change
|
||||
} else if (jv_get_kind(t) == JV_KIND_ARRAY) {
|
||||
@@ -247,7 +285,7 @@ jv jv_dels(jv t, jv keys) {
|
||||
jv_free(k);
|
||||
break;
|
||||
}
|
||||
t = jv_object_delete(t, k);
|
||||
t = jv_object_delete(t, k);
|
||||
}
|
||||
} else {
|
||||
jv err = jv_invalid_with_msg(jv_string_fmt("Cannot delete fields from %s",
|
||||
@@ -278,7 +316,7 @@ jv jv_setpath(jv root, jv path, jv value) {
|
||||
}
|
||||
jv pathcurr = jv_array_get(jv_copy(path), 0);
|
||||
jv pathrest = jv_array_slice(path, 1, jv_array_length(jv_copy(path)));
|
||||
return jv_set(root, pathcurr,
|
||||
return jv_set(root, pathcurr,
|
||||
jv_setpath(jv_get(jv_copy(root), jv_copy(pathcurr)), pathrest, value));
|
||||
}
|
||||
|
||||
@@ -344,7 +382,7 @@ static jv delpaths_sorted(jv object, jv paths, int start) {
|
||||
jv_free(paths);
|
||||
if (jv_is_valid(object))
|
||||
object = jv_dels(object, delkeys);
|
||||
else
|
||||
else
|
||||
jv_free(delkeys);
|
||||
return object;
|
||||
}
|
||||
@@ -388,6 +426,18 @@ static int string_cmp(const void* pa, const void* pb){
|
||||
return r;
|
||||
}
|
||||
|
||||
jv jv_keys_unsorted(jv x) {
|
||||
if (jv_get_kind(x) != JV_KIND_OBJECT)
|
||||
return jv_keys(x);
|
||||
jv answer = jv_array_sized(jv_object_length(jv_copy(x)));
|
||||
jv_object_foreach(x, key, value) {
|
||||
answer = jv_array_append(answer, key);
|
||||
jv_free(value);
|
||||
}
|
||||
jv_free(x);
|
||||
return answer;
|
||||
}
|
||||
|
||||
jv jv_keys(jv x) {
|
||||
if (jv_get_kind(x) == JV_KIND_OBJECT) {
|
||||
int nkeys = jv_object_length(jv_copy(x));
|
||||
@@ -438,7 +488,7 @@ int jv_cmp(jv a, jv b) {
|
||||
|
||||
case JV_KIND_NUMBER: {
|
||||
double da = jv_number_value(a), db = jv_number_value(b);
|
||||
|
||||
|
||||
// handle NaN as though it were null
|
||||
if (da != da) r = jv_cmp(jv_null(), jv_number(db));
|
||||
else if (db != db) r = jv_cmp(jv_number(da), jv_null());
|
||||
@@ -545,21 +595,23 @@ jv jv_group(jv objects, jv keys) {
|
||||
int n = jv_array_length(jv_copy(objects));
|
||||
struct sort_entry* entries = sort_items(objects, keys);
|
||||
jv ret = jv_array();
|
||||
jv curr_key = entries[0].key;
|
||||
jv group = jv_array_append(jv_array(), entries[0].object);
|
||||
for (int i = 1; i < n; i++) {
|
||||
if (jv_equal(jv_copy(curr_key), jv_copy(entries[i].key))) {
|
||||
jv_free(entries[i].key);
|
||||
} else {
|
||||
jv_free(curr_key);
|
||||
curr_key = entries[i].key;
|
||||
ret = jv_array_append(ret, group);
|
||||
group = jv_array();
|
||||
if (n > 0) {
|
||||
jv curr_key = entries[0].key;
|
||||
jv group = jv_array_append(jv_array(), entries[0].object);
|
||||
for (int i = 1; i < n; i++) {
|
||||
if (jv_equal(jv_copy(curr_key), jv_copy(entries[i].key))) {
|
||||
jv_free(entries[i].key);
|
||||
} else {
|
||||
jv_free(curr_key);
|
||||
curr_key = entries[i].key;
|
||||
ret = jv_array_append(ret, group);
|
||||
group = jv_array();
|
||||
}
|
||||
group = jv_array_append(group, entries[i].object);
|
||||
}
|
||||
group = jv_array_append(group, entries[i].object);
|
||||
jv_free(curr_key);
|
||||
ret = jv_array_append(ret, group);
|
||||
}
|
||||
jv_free(curr_key);
|
||||
ret = jv_array_append(ret, group);
|
||||
jv_mem_free(entries);
|
||||
return ret;
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#ifndef JV_AUX_H
|
||||
#define JV_AUX_H
|
||||
|
||||
#include "jv.h"
|
||||
|
||||
jv jv_get(jv t, jv k);
|
||||
jv jv_set(jv t, jv k, jv v);
|
||||
jv jv_has(jv t, jv k);
|
||||
jv jv_setpath(jv root, jv path, jv value);
|
||||
jv jv_getpath(jv root, jv path);
|
||||
jv jv_delpaths(jv root, jv paths);
|
||||
|
||||
jv jv_keys(jv /*object or array*/);
|
||||
int jv_cmp(jv, jv);
|
||||
|
||||
jv jv_group(jv objects, jv keys);
|
||||
jv jv_sort(jv objects, jv keys);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -187,7 +187,6 @@
|
||||
* used for input more than STRTOD_DIGLIM digits long (default 40).
|
||||
*/
|
||||
|
||||
#define IEEE_8087
|
||||
#define NO_ERRNO
|
||||
#define NO_HEX_FP
|
||||
#define No_Hex_NaN
|
||||
@@ -526,7 +525,7 @@ void jvp_dtoa_context_init(struct dtoa_context* C) {
|
||||
}
|
||||
C->p5s = 0;
|
||||
}
|
||||
|
||||
|
||||
static Bigint *
|
||||
Balloc(struct dtoa_context* C, int k)
|
||||
{
|
||||
@@ -4225,7 +4224,7 @@ jvp_dtoa
|
||||
while((*b++ = *s++));
|
||||
goto done0;
|
||||
}
|
||||
if (decpt <= -4 || decpt > se - s + 5) {
|
||||
if (decpt <= -4 || decpt > se - s + 15) {
|
||||
*b++ = *s++;
|
||||
if (*s) {
|
||||
*b++ = '.';
|
||||
|
||||
@@ -17,6 +17,6 @@ double jvp_strtod(struct dtoa_context* C, const char* s, char** se);
|
||||
char* jvp_dtoa(struct dtoa_context* C, double dd, int mode, int ndigits, int *decpt, int *sign, char **rve);
|
||||
void jvp_freedtoa(struct dtoa_context* C, char *s);
|
||||
|
||||
#define JVP_DTOA_FMT_MAX_LEN 32
|
||||
#define JVP_DTOA_FMT_MAX_LEN 64
|
||||
char* jvp_dtoa_fmt(struct dtoa_context* C, register char *b, double x);
|
||||
#endif
|
||||
@@ -0,0 +1,49 @@
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "jv.h"
|
||||
|
||||
jv jv_load_file(const char* filename, int raw) {
|
||||
FILE* file = fopen(filename, "r");
|
||||
struct jv_parser* parser;
|
||||
jv data;
|
||||
if (!file) {
|
||||
return jv_invalid_with_msg(jv_string_fmt("Could not open %s: %s",
|
||||
filename,
|
||||
strerror(errno)));
|
||||
}
|
||||
if (raw) {
|
||||
data = jv_string("");
|
||||
} else {
|
||||
data = jv_array();
|
||||
parser = jv_parser_new(0);
|
||||
}
|
||||
while (!feof(file) && !ferror(file)) {
|
||||
char buf[4096];
|
||||
size_t n = fread(buf, 1, sizeof(buf), file);
|
||||
if (raw) {
|
||||
data = jv_string_concat(data, jv_string_sized(buf, (int)n));
|
||||
} else {
|
||||
jv_parser_set_buf(parser, buf, n, !feof(file));
|
||||
jv value;
|
||||
while (jv_is_valid((value = jv_parser_next(parser))))
|
||||
data = jv_array_append(data, value);
|
||||
if (jv_invalid_has_msg(jv_copy(value))) {
|
||||
jv_free(data);
|
||||
data = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!raw)
|
||||
jv_parser_free(parser);
|
||||
int badread = ferror(file);
|
||||
if (fclose(file) != 0 || badread) {
|
||||
jv_free(data);
|
||||
return jv_invalid_with_msg(jv_string_fmt("Error reading from %s",
|
||||
filename));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
+437
-31
@@ -1,11 +1,12 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "jv.h"
|
||||
#include "jv_dtoa.h"
|
||||
#include "jv_parse.h"
|
||||
#include "jv_unicode.h"
|
||||
#include "jv_alloc.h"
|
||||
#include "jv_dtoa.h"
|
||||
|
||||
typedef const char* presult;
|
||||
|
||||
@@ -16,32 +17,118 @@ typedef const char* presult;
|
||||
#define pfunc presult
|
||||
#endif
|
||||
|
||||
void jv_parser_init(struct jv_parser* p) {
|
||||
enum last_seen {
|
||||
JV_LAST_NONE = 0,
|
||||
JV_LAST_OPEN_ARRAY = '[',
|
||||
JV_LAST_OPEN_OBJECT = '{',
|
||||
JV_LAST_COLON = ':',
|
||||
JV_LAST_COMMA = ',',
|
||||
JV_LAST_VALUE = 'V',
|
||||
};
|
||||
|
||||
struct jv_parser {
|
||||
const char* curr_buf;
|
||||
int curr_buf_length;
|
||||
int curr_buf_pos;
|
||||
int curr_buf_is_partial;
|
||||
int eof;
|
||||
unsigned bom_strip_position;
|
||||
|
||||
int flags;
|
||||
|
||||
jv* stack; // parser
|
||||
int stackpos; // parser
|
||||
int stacklen; // both (optimization; it's really pathlen for streaming)
|
||||
jv path; // streamer
|
||||
enum last_seen last_seen; // streamer
|
||||
jv output; // streamer
|
||||
jv next; // both
|
||||
|
||||
char* tokenbuf;
|
||||
int tokenpos;
|
||||
int tokenlen;
|
||||
|
||||
int line, column;
|
||||
|
||||
struct dtoa_context dtoa;
|
||||
|
||||
enum {
|
||||
JV_PARSER_NORMAL,
|
||||
JV_PARSER_STRING,
|
||||
JV_PARSER_STRING_ESCAPE,
|
||||
JV_PARSER_WAITING_FOR_RS // parse error, waiting for RS
|
||||
} st;
|
||||
unsigned int last_ch_was_ws:1;
|
||||
};
|
||||
|
||||
|
||||
static void parser_init(struct jv_parser* p, int flags) {
|
||||
p->flags = flags;
|
||||
if ((p->flags & JV_PARSE_STREAMING)) {
|
||||
p->path = jv_array();
|
||||
} else {
|
||||
p->path = jv_invalid();
|
||||
p->flags &= ~(JV_PARSE_STREAM_ERRORS);
|
||||
}
|
||||
p->stack = 0;
|
||||
p->stacklen = p->stackpos = 0;
|
||||
p->last_seen = JV_LAST_NONE;
|
||||
p->output = jv_invalid();
|
||||
p->next = jv_invalid();
|
||||
p->tokenbuf = 0;
|
||||
p->tokenlen = p->tokenpos = 0;
|
||||
p->st = JV_PARSER_NORMAL;
|
||||
if ((p->flags & JV_PARSE_SEQ))
|
||||
p->st = JV_PARSER_WAITING_FOR_RS;
|
||||
else
|
||||
p->st = JV_PARSER_NORMAL;
|
||||
p->eof = 0;
|
||||
p->curr_buf = 0;
|
||||
p->curr_buf_length = p->curr_buf_pos = p->curr_buf_is_partial = 0;
|
||||
p->bom_strip_position = 0;
|
||||
p->last_ch_was_ws = 0;
|
||||
p->line = 1;
|
||||
p->column = 0;
|
||||
jvp_dtoa_context_init(&p->dtoa);
|
||||
}
|
||||
|
||||
void jv_parser_free(struct jv_parser* p) {
|
||||
static void parser_reset(struct jv_parser* p) {
|
||||
if ((p->flags & JV_PARSE_STREAMING)) {
|
||||
jv_free(p->path);
|
||||
p->path = jv_array();
|
||||
p->stacklen = 0;
|
||||
}
|
||||
p->last_seen = JV_LAST_NONE;
|
||||
jv_free(p->output);
|
||||
p->output = jv_invalid();
|
||||
jv_free(p->next);
|
||||
for (int i=0; i<p->stackpos; i++)
|
||||
p->next = jv_invalid();
|
||||
for (int i=0; i<p->stackpos; i++)
|
||||
jv_free(p->stack[i]);
|
||||
p->stackpos = 0;
|
||||
p->tokenpos = 0;
|
||||
p->st = JV_PARSER_NORMAL;
|
||||
}
|
||||
|
||||
static void parser_free(struct jv_parser* p) {
|
||||
parser_reset(p);
|
||||
jv_free(p->path);
|
||||
jv_free(p->output);
|
||||
jv_mem_free(p->stack);
|
||||
jv_mem_free(p->tokenbuf);
|
||||
jvp_dtoa_context_free(&p->dtoa);
|
||||
}
|
||||
|
||||
static pfunc value(struct jv_parser* p, jv val) {
|
||||
if (jv_is_valid(p->next)) return "Expected separator between values";
|
||||
if ((p->flags & JV_PARSE_STREAMING)) {
|
||||
if (jv_is_valid(p->next) || p->last_seen == JV_LAST_VALUE)
|
||||
return "Expected separator between values";
|
||||
if (p->stacklen > 0)
|
||||
p->last_seen = JV_LAST_VALUE;
|
||||
else
|
||||
p->last_seen = JV_LAST_NONE;
|
||||
} else {
|
||||
if (jv_is_valid(p->next)) return "Expected separator between values";
|
||||
}
|
||||
jv_free(p->next);
|
||||
p->next = val;
|
||||
return 0;
|
||||
@@ -57,7 +144,7 @@ static void push(struct jv_parser* p, jv v) {
|
||||
p->stack[p->stackpos++] = v;
|
||||
}
|
||||
|
||||
static pfunc token(struct jv_parser* p, char ch) {
|
||||
static pfunc parse_token(struct jv_parser* p, char ch) {
|
||||
switch (ch) {
|
||||
case '[':
|
||||
if (jv_is_valid(p->next)) return "Expected separator between values";
|
||||
@@ -70,7 +157,7 @@ static pfunc token(struct jv_parser* p, char ch) {
|
||||
break;
|
||||
|
||||
case ':':
|
||||
if (!jv_is_valid(p->next))
|
||||
if (!jv_is_valid(p->next))
|
||||
return "Expected string key before ':'";
|
||||
if (p->stackpos == 0 || jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_OBJECT)
|
||||
return "':' not as part of an object";
|
||||
@@ -90,7 +177,7 @@ static pfunc token(struct jv_parser* p, char ch) {
|
||||
p->next = jv_invalid();
|
||||
} else if (jv_get_kind(p->stack[p->stackpos-1]) == JV_KIND_STRING) {
|
||||
assert(p->stackpos > 1 && jv_get_kind(p->stack[p->stackpos-2]) == JV_KIND_OBJECT);
|
||||
p->stack[p->stackpos-2] = jv_object_set(p->stack[p->stackpos-2],
|
||||
p->stack[p->stackpos-2] = jv_object_set(p->stack[p->stackpos-2],
|
||||
p->stack[p->stackpos-1], p->next);
|
||||
p->stackpos--;
|
||||
p->next = jv_invalid();
|
||||
@@ -123,7 +210,7 @@ static pfunc token(struct jv_parser* p, char ch) {
|
||||
if (jv_get_kind(p->stack[p->stackpos-1]) != JV_KIND_STRING)
|
||||
return "Objects must consist of key:value pairs";
|
||||
assert(p->stackpos > 1 && jv_get_kind(p->stack[p->stackpos-2]) == JV_KIND_OBJECT);
|
||||
p->stack[p->stackpos-2] = jv_object_set(p->stack[p->stackpos-2],
|
||||
p->stack[p->stackpos-2] = jv_object_set(p->stack[p->stackpos-2],
|
||||
p->stack[p->stackpos-1], p->next);
|
||||
p->stackpos--;
|
||||
p->next = jv_invalid();
|
||||
@@ -140,6 +227,159 @@ static pfunc token(struct jv_parser* p, char ch) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static pfunc stream_token(struct jv_parser* p, char ch) {
|
||||
jv_kind k;
|
||||
jv last;
|
||||
|
||||
switch (ch) {
|
||||
case '[':
|
||||
if (jv_is_valid(p->next))
|
||||
return "Expected a separator between values";
|
||||
p->path = jv_array_append(p->path, jv_number(0)); // push
|
||||
p->last_seen = JV_LAST_OPEN_ARRAY;
|
||||
p->stacklen++;
|
||||
break;
|
||||
|
||||
case '{':
|
||||
if (p->last_seen == JV_LAST_VALUE)
|
||||
return "Expected a separator between values";
|
||||
// Push object key: null, since we don't know it yet
|
||||
p->path = jv_array_append(p->path, jv_null()); // push
|
||||
p->last_seen = JV_LAST_OPEN_OBJECT;
|
||||
p->stacklen++;
|
||||
break;
|
||||
|
||||
case ':':
|
||||
if (p->stacklen == 0 || jv_get_kind(jv_array_get(jv_copy(p->path), p->stacklen - 1)) == JV_KIND_NUMBER)
|
||||
return "':' not as part of an object";
|
||||
if (!jv_is_valid(p->next) || p->last_seen == JV_LAST_NONE)
|
||||
return "Expected string key before ':'";
|
||||
if (jv_get_kind(p->next) != JV_KIND_STRING)
|
||||
return "Object keys must be strings";
|
||||
if (p->last_seen != JV_LAST_VALUE)
|
||||
return "':' should follow a key";
|
||||
p->last_seen = JV_LAST_COLON;
|
||||
p->path = jv_array_set(p->path, p->stacklen - 1, p->next);
|
||||
p->next = jv_invalid();
|
||||
break;
|
||||
|
||||
case ',':
|
||||
if (p->last_seen != JV_LAST_VALUE)
|
||||
return "Expected value before ','";
|
||||
if (p->stacklen == 0)
|
||||
return "',' not as part of an object or array";
|
||||
last = jv_array_get(jv_copy(p->path), p->stacklen - 1);
|
||||
k = jv_get_kind(last);
|
||||
if (k == JV_KIND_NUMBER) {
|
||||
int idx = jv_number_value(last);
|
||||
|
||||
if (jv_is_valid(p->next)) {
|
||||
p->output = JV_ARRAY(jv_copy(p->path), p->next);
|
||||
p->next = jv_invalid();
|
||||
}
|
||||
p->path = jv_array_set(p->path, p->stacklen - 1, jv_number(idx + 1));
|
||||
p->last_seen = JV_LAST_COMMA;
|
||||
} else if (k == JV_KIND_STRING) {
|
||||
if (jv_is_valid(p->next)) {
|
||||
p->output = JV_ARRAY(jv_copy(p->path), p->next);
|
||||
p->next = jv_invalid();
|
||||
}
|
||||
p->path = jv_array_set(p->path, p->stacklen - 1, jv_true()); // ready for another name:value pair
|
||||
p->last_seen = JV_LAST_COMMA;
|
||||
} else {
|
||||
assert(k == JV_KIND_NULL);
|
||||
// this case hits on input like {,}
|
||||
// make sure to handle input like {"a", "b"} and {"a":, ...}
|
||||
jv_free(last);
|
||||
return "Objects must consist of key:value pairs";
|
||||
}
|
||||
jv_free(last);
|
||||
break;
|
||||
|
||||
case ']':
|
||||
if (p->stacklen == 0)
|
||||
return "Unmatched ']' at the top-level";
|
||||
if (p->last_seen == JV_LAST_COMMA)
|
||||
return "Expected another array element";
|
||||
if (p->last_seen == JV_LAST_OPEN_ARRAY)
|
||||
assert(!jv_is_valid(p->next));
|
||||
|
||||
last = jv_array_get(jv_copy(p->path), p->stacklen - 1);
|
||||
k = jv_get_kind(last);
|
||||
jv_free(last);
|
||||
|
||||
if (k != JV_KIND_NUMBER)
|
||||
return "Unmatched ']' in the middle of an object";
|
||||
if (jv_is_valid(p->next)) {
|
||||
p->output = JV_ARRAY(jv_copy(p->path), p->next, jv_true());
|
||||
p->next = jv_invalid();
|
||||
} else if (p->last_seen != JV_LAST_OPEN_ARRAY) {
|
||||
p->output = JV_ARRAY(jv_copy(p->path));
|
||||
}
|
||||
|
||||
p->path = jv_array_slice(p->path, 0, --(p->stacklen)); // pop
|
||||
//assert(!jv_is_valid(p->next));
|
||||
jv_free(p->next);
|
||||
p->next = jv_invalid();
|
||||
|
||||
if (p->last_seen == JV_LAST_OPEN_ARRAY)
|
||||
p->output = JV_ARRAY(jv_copy(p->path), jv_array()); // Empty arrays are leaves
|
||||
|
||||
if (p->stacklen == 0)
|
||||
p->last_seen = JV_LAST_NONE;
|
||||
else
|
||||
p->last_seen = JV_LAST_VALUE;
|
||||
break;
|
||||
|
||||
case '}':
|
||||
if (p->stacklen == 0)
|
||||
return "Unmatched '}' at the top-level";
|
||||
if (p->last_seen == JV_LAST_COMMA)
|
||||
return "Expected another key:value pair";
|
||||
if (p->last_seen == JV_LAST_OPEN_OBJECT)
|
||||
assert(!jv_is_valid(p->next));
|
||||
|
||||
last = jv_array_get(jv_copy(p->path), p->stacklen - 1);
|
||||
k = jv_get_kind(last);
|
||||
jv_free(last);
|
||||
if (k == JV_KIND_NUMBER)
|
||||
return "Unmatched '}' in the middle of an array";
|
||||
|
||||
if (jv_is_valid(p->next)) {
|
||||
if (k != JV_KIND_STRING)
|
||||
return "Objects must consist of key:value pairs";
|
||||
p->output = JV_ARRAY(jv_copy(p->path), p->next, jv_true());
|
||||
p->next = jv_invalid();
|
||||
} else {
|
||||
// Perhaps {"a":[]}
|
||||
if (p->last_seen == JV_LAST_COLON)
|
||||
// Looks like {"a":}
|
||||
return "Missing value in key:value pair";
|
||||
if (p->last_seen == JV_LAST_COMMA)
|
||||
// Looks like {"a":0,}
|
||||
return "Expected another key-value pair";
|
||||
if (p->last_seen == JV_LAST_OPEN_ARRAY)
|
||||
return "Unmatched '}' in the middle of an array";
|
||||
if (p->last_seen != JV_LAST_VALUE && p->last_seen != JV_LAST_OPEN_OBJECT)
|
||||
return "Unmatched '}'";
|
||||
if (p->last_seen != JV_LAST_OPEN_OBJECT)
|
||||
p->output = JV_ARRAY(jv_copy(p->path));
|
||||
}
|
||||
p->path = jv_array_slice(p->path, 0, --(p->stacklen)); // pop
|
||||
jv_free(p->next);
|
||||
p->next = jv_invalid();
|
||||
|
||||
if (p->last_seen == JV_LAST_OPEN_OBJECT)
|
||||
p->output = JV_ARRAY(jv_copy(p->path), jv_object()); // Empty arrays are leaves
|
||||
|
||||
if (p->stacklen == 0)
|
||||
p->last_seen = JV_LAST_NONE;
|
||||
else
|
||||
p->last_seen = JV_LAST_VALUE;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tokenadd(struct jv_parser* p, char c) {
|
||||
assert(p->tokenpos <= p->tokenlen);
|
||||
@@ -170,7 +410,7 @@ static pfunc found_string(struct jv_parser* p) {
|
||||
char* in = p->tokenbuf;
|
||||
char* out = p->tokenbuf;
|
||||
char* end = p->tokenbuf + p->tokenpos;
|
||||
|
||||
|
||||
while (in < end) {
|
||||
char c = *in++;
|
||||
if (c == '\\') {
|
||||
@@ -207,7 +447,8 @@ static pfunc found_string(struct jv_parser* p) {
|
||||
codepoint = 0x10000 + (((codepoint - 0xD800) << 10)
|
||||
|(surrogate - 0xDC00));
|
||||
}
|
||||
// FIXME assert valid codepoint
|
||||
if (codepoint > 0x10FFFF)
|
||||
codepoint = 0xFFFD; // U+FFFD REPLACEMENT CHARACTER
|
||||
out += jvp_utf8_encode(codepoint, out);
|
||||
break;
|
||||
|
||||
@@ -215,6 +456,8 @@ static pfunc found_string(struct jv_parser* p) {
|
||||
return "Invalid escape";
|
||||
}
|
||||
} else {
|
||||
if (c > 0 && c < 0x001f)
|
||||
return "Invalid string: control characters from U+0000 through U+001F must be escaped";
|
||||
*out++ = c;
|
||||
}
|
||||
}
|
||||
@@ -236,7 +479,7 @@ static pfunc check_literal(struct jv_parser* p) {
|
||||
}
|
||||
if (pattern) {
|
||||
if (p->tokenpos != plen) return "Invalid literal";
|
||||
for (int i=0; i<plen; i++)
|
||||
for (int i=0; i<plen; i++)
|
||||
if (p->tokenbuf[i] != pattern[i])
|
||||
return "Invalid literal";
|
||||
TRY(value(p, v));
|
||||
@@ -285,7 +528,7 @@ static chclass classify(char c) {
|
||||
|
||||
static const presult OK = "output produced";
|
||||
|
||||
static int check_done(struct jv_parser* p, jv* out) {
|
||||
static int parse_check_done(struct jv_parser* p, jv* out) {
|
||||
if (p->stackpos == 0 && jv_is_valid(p->next)) {
|
||||
*out = p->next;
|
||||
p->next = jv_invalid();
|
||||
@@ -295,15 +538,85 @@ static int check_done(struct jv_parser* p, jv* out) {
|
||||
}
|
||||
}
|
||||
|
||||
static int stream_check_done(struct jv_parser* p, jv* out) {
|
||||
if (p->stacklen == 0 && jv_is_valid(p->next)) {
|
||||
*out = JV_ARRAY(jv_copy(p->path),p->next);
|
||||
p->next = jv_invalid();
|
||||
return 1;
|
||||
} else if (jv_is_valid(p->output)) {
|
||||
if (jv_array_length(jv_copy(p->output)) > 2) {
|
||||
// At end of an array or object, necessitating one more output by
|
||||
// which to indicate this
|
||||
*out = jv_array_slice(jv_copy(p->output), 0, 2);
|
||||
p->output = jv_array_slice(p->output, 0, 1); // arrange one more output
|
||||
} else {
|
||||
// No further processing needed
|
||||
*out = p->output;
|
||||
p->output = jv_invalid();
|
||||
}
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_check_truncation(struct jv_parser* p) {
|
||||
return ((p->flags & JV_PARSE_SEQ) && !p->last_ch_was_ws && (p->stackpos > 0 || p->tokenpos > 0 || jv_get_kind(p->next) == JV_KIND_NUMBER));
|
||||
}
|
||||
|
||||
static int stream_check_truncation(struct jv_parser* p) {
|
||||
jv_kind k = jv_get_kind(p->next);
|
||||
return (p->stacklen > 0 || k == JV_KIND_NUMBER || k == JV_KIND_TRUE || k == JV_KIND_FALSE || k == JV_KIND_NULL);
|
||||
}
|
||||
|
||||
static int parse_is_top_num(struct jv_parser* p) {
|
||||
return (p->stackpos == 0 && jv_get_kind(p->next) == JV_KIND_NUMBER);
|
||||
}
|
||||
|
||||
static int stream_is_top_num(struct jv_parser* p) {
|
||||
return (p->stacklen == 0 && jv_get_kind(p->next) == JV_KIND_NUMBER);
|
||||
}
|
||||
|
||||
#define check_done(p, o) \
|
||||
(((p)->flags & JV_PARSE_STREAMING) ? stream_check_done((p), (o)) : parse_check_done((p), (o)))
|
||||
|
||||
#define token(p, ch) \
|
||||
(((p)->flags & JV_PARSE_STREAMING) ? stream_token((p), (ch)) : parse_token((p), (ch)))
|
||||
|
||||
#define check_truncation(p) \
|
||||
(((p)->flags & JV_PARSE_STREAMING) ? stream_check_truncation((p)) : parse_check_truncation((p)))
|
||||
|
||||
#define is_top_num(p) \
|
||||
(((p)->flags & JV_PARSE_STREAMING) ? stream_is_top_num((p)) : parse_is_top_num((p)))
|
||||
|
||||
static pfunc scan(struct jv_parser* p, char ch, jv* out) {
|
||||
p->column++;
|
||||
if (ch == '\n') {
|
||||
p->line++;
|
||||
p->column = 0;
|
||||
}
|
||||
if (ch == '\036' /* ASCII RS; see draft-ietf-json-sequence-07 */) {
|
||||
if (check_truncation(p)) {
|
||||
if (check_literal(p) == 0 && is_top_num(p))
|
||||
return "Potentially truncated top-level numeric value";
|
||||
return "Truncated value";
|
||||
}
|
||||
TRY(check_literal(p));
|
||||
if (p->st == JV_PARSER_NORMAL && check_done(p, out))
|
||||
return OK;
|
||||
// shouldn't happen?
|
||||
assert(!jv_is_valid(*out));
|
||||
parser_reset(p);
|
||||
jv_free(*out);
|
||||
*out = jv_invalid();
|
||||
return OK;
|
||||
}
|
||||
presult answer = 0;
|
||||
p->last_ch_was_ws = 0;
|
||||
if (p->st == JV_PARSER_NORMAL) {
|
||||
chclass cls = classify(ch);
|
||||
if (cls == WHITESPACE)
|
||||
p->last_ch_was_ws = 1;
|
||||
if (cls != LITERAL) {
|
||||
TRY(check_literal(p));
|
||||
if (check_done(p, out)) answer = OK;
|
||||
@@ -341,12 +654,30 @@ static pfunc scan(struct jv_parser* p, char ch, jv* out) {
|
||||
return answer;
|
||||
}
|
||||
|
||||
struct jv_parser* jv_parser_new(int flags) {
|
||||
struct jv_parser* p = jv_mem_alloc(sizeof(struct jv_parser));
|
||||
parser_init(p, flags);
|
||||
p->flags = flags;
|
||||
return p;
|
||||
}
|
||||
|
||||
void jv_parser_free(struct jv_parser* p) {
|
||||
parser_free(p);
|
||||
jv_mem_free(p);
|
||||
}
|
||||
|
||||
static const unsigned char UTF8_BOM[] = {0xEF,0xBB,0xBF};
|
||||
|
||||
int jv_parser_remaining(struct jv_parser* p) {
|
||||
if (p->curr_buf == 0)
|
||||
return 0;
|
||||
return (p->curr_buf_length - p->curr_buf_pos);
|
||||
}
|
||||
|
||||
void jv_parser_set_buf(struct jv_parser* p, const char* buf, int length, int is_partial) {
|
||||
assert((p->curr_buf == 0 || p->curr_buf_pos == p->curr_buf_length)
|
||||
&& "previous buffer not exhausted");
|
||||
while (p->bom_strip_position < sizeof(UTF8_BOM)) {
|
||||
while (length > 0 && p->bom_strip_position < sizeof(UTF8_BOM)) {
|
||||
if ((unsigned char)*buf == UTF8_BOM[p->bom_strip_position]) {
|
||||
// matched a BOM character
|
||||
buf++;
|
||||
@@ -368,43 +699,118 @@ void jv_parser_set_buf(struct jv_parser* p, const char* buf, int length, int is_
|
||||
p->curr_buf_is_partial = is_partial;
|
||||
}
|
||||
|
||||
static jv make_error(struct jv_parser*, const char *, ...) JV_PRINTF_LIKE(2, 3);
|
||||
|
||||
static jv make_error(struct jv_parser* p, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
jv e = jv_string_vfmt(fmt, ap);
|
||||
va_end(ap);
|
||||
if ((p->flags & JV_PARSE_STREAM_ERRORS))
|
||||
return JV_ARRAY(e, jv_copy(p->path));
|
||||
return jv_invalid_with_msg(e);
|
||||
}
|
||||
|
||||
jv jv_parser_next(struct jv_parser* p) {
|
||||
assert(p->curr_buf && "a buffer must be provided");
|
||||
if (p->bom_strip_position == 0xff) return jv_invalid_with_msg(jv_string("Malformed BOM"));
|
||||
jv value;
|
||||
if (p->eof)
|
||||
return jv_invalid();
|
||||
if (!p->curr_buf)
|
||||
return jv_invalid(); // Need a buffer
|
||||
if (p->bom_strip_position == 0xff) {
|
||||
if (!(p->flags & JV_PARSE_SEQ))
|
||||
return jv_invalid_with_msg(jv_string("Malformed BOM"));
|
||||
p->st =JV_PARSER_WAITING_FOR_RS;
|
||||
parser_reset(p);
|
||||
}
|
||||
jv value = jv_invalid();
|
||||
if ((p->flags & JV_PARSE_STREAMING) && stream_check_done(p, &value))
|
||||
return value;
|
||||
char ch;
|
||||
presult msg = 0;
|
||||
while (!msg && p->curr_buf_pos < p->curr_buf_length) {
|
||||
char ch = p->curr_buf[p->curr_buf_pos++];
|
||||
ch = p->curr_buf[p->curr_buf_pos++];
|
||||
if (p->st == JV_PARSER_WAITING_FOR_RS) {
|
||||
if (ch == '\n') {
|
||||
p->line++;
|
||||
p->column = 0;
|
||||
} else {
|
||||
p->column++;
|
||||
}
|
||||
if (ch == '\036')
|
||||
p->st = JV_PARSER_NORMAL;
|
||||
continue; // need to resync, wait for RS
|
||||
}
|
||||
msg = scan(p, ch, &value);
|
||||
}
|
||||
if (msg == OK) {
|
||||
return value;
|
||||
} else if (msg) {
|
||||
return jv_invalid_with_msg(jv_string_fmt("%s at line %d, column %d", msg, p->line, p->column));
|
||||
jv_free(value);
|
||||
if (ch != '\036' && (p->flags & JV_PARSE_SEQ)) {
|
||||
// Skip to the next RS
|
||||
p->st = JV_PARSER_WAITING_FOR_RS;
|
||||
value = make_error(p, "%s at line %d, column %d (need RS to resync)", msg, p->line, p->column);
|
||||
parser_reset(p);
|
||||
return value;
|
||||
}
|
||||
value = make_error(p, "%s at line %d, column %d", msg, p->line, p->column);
|
||||
parser_reset(p);
|
||||
if (!(p->flags & JV_PARSE_SEQ)) {
|
||||
// We're not parsing a JSON text sequence; throw this buffer away.
|
||||
// XXX We should fail permanently here.
|
||||
p->curr_buf = 0;
|
||||
p->curr_buf_pos = 0;
|
||||
} // Else ch must be RS; don't clear buf so we can start parsing again after this ch
|
||||
return value;
|
||||
} else if (p->curr_buf_is_partial) {
|
||||
assert(p->curr_buf_pos == p->curr_buf_length);
|
||||
// need another buffer
|
||||
return jv_invalid();
|
||||
} else {
|
||||
assert(p->curr_buf_pos == p->curr_buf_length);
|
||||
// at EOF
|
||||
if (p->st != JV_PARSER_NORMAL)
|
||||
return jv_invalid_with_msg(jv_string("Unfinished string"));
|
||||
if ((msg = check_literal(p)))
|
||||
return jv_invalid_with_msg(jv_string(msg));
|
||||
if (p->stackpos != 0)
|
||||
return jv_invalid_with_msg(jv_string("Unfinished JSON term"));
|
||||
// p->next is either invalid (nothing here but no syntax error)
|
||||
p->eof = 1;
|
||||
assert(p->curr_buf_pos == p->curr_buf_length);
|
||||
jv_free(value);
|
||||
if (p->st == JV_PARSER_WAITING_FOR_RS)
|
||||
return make_error(p, "Unfinished abandoned text at EOF at line %d, column %d", p->line, p->column);
|
||||
if (p->st != JV_PARSER_NORMAL) {
|
||||
value = make_error(p, "Unfinished string at EOF at line %d, column %d", p->line, p->column);
|
||||
parser_reset(p);
|
||||
p->st = JV_PARSER_WAITING_FOR_RS;
|
||||
return value;
|
||||
}
|
||||
if ((msg = check_literal(p))) {
|
||||
value = make_error(p, "%s at EOF at line %d, column %d", msg, p->line, p->column);
|
||||
parser_reset(p);
|
||||
p->st = JV_PARSER_WAITING_FOR_RS;
|
||||
return value;
|
||||
}
|
||||
if (((p->flags & JV_PARSE_STREAMING) && p->stacklen != 0) ||
|
||||
(!(p->flags & JV_PARSE_STREAMING) && p->stackpos != 0)) {
|
||||
value = make_error(p, "Unfinished JSON term at EOF at line %d, column %d", p->line, p->column);
|
||||
parser_reset(p);
|
||||
p->st = JV_PARSER_WAITING_FOR_RS;
|
||||
return value;
|
||||
}
|
||||
// p->next is either invalid (nothing here, but no syntax error)
|
||||
// or valid (this is the value). either way it's the thing to return
|
||||
value = p->next;
|
||||
if ((p->flags & JV_PARSE_STREAMING) && jv_is_valid(p->next)) {
|
||||
value = JV_ARRAY(jv_copy(p->path), p->next); // except in streaming mode we've got to make it [path,value]
|
||||
} else {
|
||||
value = p->next;
|
||||
}
|
||||
p->next = jv_invalid();
|
||||
if ((p->flags & JV_PARSE_SEQ) && !p->last_ch_was_ws && jv_get_kind(value) == JV_KIND_NUMBER) {
|
||||
jv_free(value);
|
||||
return make_error(p, "Potentially truncated top-level numeric value at EOF at line %d, column %d", p->line, p->column);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
jv jv_parse_sized(const char* string, int length) {
|
||||
struct jv_parser parser;
|
||||
jv_parser_init(&parser);
|
||||
parser_init(&parser, 0);
|
||||
jv_parser_set_buf(&parser, string, length, 0);
|
||||
jv value = jv_parser_next(&parser);
|
||||
if (jv_is_valid(value)) {
|
||||
@@ -429,7 +835,7 @@ jv jv_parse_sized(const char* string, int length) {
|
||||
jv_free(value);
|
||||
value = jv_invalid_with_msg(jv_string("Expected JSON value"));
|
||||
}
|
||||
jv_parser_free(&parser);
|
||||
parser_free(&parser);
|
||||
|
||||
if (!jv_is_valid(value) && jv_invalid_has_msg(jv_copy(value))) {
|
||||
jv msg = jv_invalid_get_msg(value);
|
||||
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
#ifndef JV_PARSE_H
|
||||
#define JV_PARSE_H
|
||||
#include "jv_dtoa.h"
|
||||
struct jv_parser {
|
||||
const char* curr_buf;
|
||||
int curr_buf_length;
|
||||
int curr_buf_pos;
|
||||
int curr_buf_is_partial;
|
||||
unsigned bom_strip_position;
|
||||
|
||||
jv* stack;
|
||||
int stackpos;
|
||||
int stacklen;
|
||||
jv next;
|
||||
|
||||
char* tokenbuf;
|
||||
int tokenpos;
|
||||
int tokenlen;
|
||||
|
||||
int line, column;
|
||||
|
||||
struct dtoa_context dtoa;
|
||||
|
||||
enum {
|
||||
JV_PARSER_NORMAL,
|
||||
JV_PARSER_STRING,
|
||||
JV_PARSER_STRING_ESCAPE
|
||||
} st;
|
||||
};
|
||||
|
||||
void jv_parser_init(struct jv_parser* p);
|
||||
void jv_parser_free(struct jv_parser* p);
|
||||
|
||||
void jv_parser_set_buf(struct jv_parser* p, const char* buf, int length, int is_partial);
|
||||
|
||||
jv jv_parser_next(struct jv_parser* p);
|
||||
#endif
|
||||
+176
-77
@@ -1,8 +1,15 @@
|
||||
#include "jv.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <float.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
#include <fileapi.h>
|
||||
#endif
|
||||
|
||||
#include "jv.h"
|
||||
#include "jv_dtoa.h"
|
||||
#include "jv_unicode.h"
|
||||
|
||||
@@ -12,75 +19,88 @@
|
||||
|
||||
// Colour table. See http://en.wikipedia.org/wiki/ANSI_escape_code#Colors
|
||||
// for how to choose these.
|
||||
static const jv_kind colour_kinds[] =
|
||||
static const jv_kind colour_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 colours[] =
|
||||
{COL("30;1"), COL("0"), COL("0"), COL("0"),
|
||||
COL("32"), COL("37"), COL("37")};
|
||||
{COL("1;30"), COL("0;39"), COL("0;39"), COL("0;39"),
|
||||
COL("0;32"), COL("1;39"), COL("1;39")};
|
||||
#define FIELD_COLOUR COL("34;1")
|
||||
|
||||
static void put_buf(const char* s, int len, FILE* fout, jv* strout) {
|
||||
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
|
||||
fwrite(s, 1, len, fout);
|
||||
#else
|
||||
fwrite(s, 1, len, fout);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void put_char(char c, FILE* fout, jv* strout) {
|
||||
put_buf(&c, 1, fout, strout);
|
||||
static void put_char(char c, FILE* fout, jv* strout, int T) {
|
||||
put_buf(&c, 1, fout, strout, T);
|
||||
}
|
||||
|
||||
static void put_str(const char* s, FILE* fout, jv* strout) {
|
||||
put_buf(s, strlen(s), fout, strout);
|
||||
static void put_str(const char* s, FILE* fout, jv* strout, int T) {
|
||||
put_buf(s, strlen(s), fout, strout, T);
|
||||
}
|
||||
|
||||
static void put_space(int n, FILE* fout, jv* strout) {
|
||||
while (n--) {
|
||||
put_char(' ', fout, strout);
|
||||
static void put_indent(int n, int flags, FILE* fout, jv* strout, int T) {
|
||||
if (flags & JV_PRINT_TAB) {
|
||||
while (n--)
|
||||
put_char('\t', fout, strout, T);
|
||||
} else {
|
||||
n *= ((flags & (JV_PRINT_SPACE0 | JV_PRINT_SPACE1 | JV_PRINT_SPACE2)) >> 8);
|
||||
while (n--)
|
||||
put_char(' ', fout, strout, T);
|
||||
}
|
||||
}
|
||||
|
||||
static void jvp_dump_string(jv str, int ascii_only, FILE* F, jv* S) {
|
||||
static void jvp_dump_string(jv str, int ascii_only, FILE* F, jv* S, int T) {
|
||||
assert(jv_get_kind(str) == JV_KIND_STRING);
|
||||
const char* i = jv_string_value(str);
|
||||
const char* end = i + jv_string_length_bytes(jv_copy(str));
|
||||
const char* cstart;
|
||||
int c = 0;
|
||||
char buf[32];
|
||||
put_char('"', F, S);
|
||||
put_char('"', F, S, T);
|
||||
while ((i = jvp_utf8_next((cstart = i), end, &c))) {
|
||||
assert(c != -1);
|
||||
int unicode_escape = 0;
|
||||
if (0x20 <= c && c <= 0x7E) {
|
||||
// printable ASCII
|
||||
if (c == '"' || c == '\\') {
|
||||
put_char('\\', F, S);
|
||||
put_char('\\', F, S, T);
|
||||
}
|
||||
put_char(c, F, S);
|
||||
put_char(c, F, S, T);
|
||||
} else if (c < 0x20 || c == 0x7F) {
|
||||
// ASCII control character
|
||||
switch (c) {
|
||||
case '\b':
|
||||
put_char('\\', F, S);
|
||||
put_char('b', F, S);
|
||||
put_char('\\', F, S, T);
|
||||
put_char('b', F, S, T);
|
||||
break;
|
||||
case '\t':
|
||||
put_char('\\', F, S);
|
||||
put_char('t', F, S);
|
||||
put_char('\\', F, S, T);
|
||||
put_char('t', F, S, T);
|
||||
break;
|
||||
case '\r':
|
||||
put_char('\\', F, S);
|
||||
put_char('r', F, S);
|
||||
put_char('\\', F, S, T);
|
||||
put_char('r', F, S, T);
|
||||
break;
|
||||
case '\n':
|
||||
put_char('\\', F, S);
|
||||
put_char('n', F, S);
|
||||
put_char('\\', F, S, T);
|
||||
put_char('n', F, S, T);
|
||||
break;
|
||||
case '\f':
|
||||
put_char('\\', F, S);
|
||||
put_char('f', F, S);
|
||||
put_char('\\', F, S, T);
|
||||
put_char('f', F, S, T);
|
||||
break;
|
||||
default:
|
||||
unicode_escape = 1;
|
||||
@@ -90,7 +110,7 @@ static void jvp_dump_string(jv str, int ascii_only, FILE* F, jv* S) {
|
||||
if (ascii_only) {
|
||||
unicode_escape = 1;
|
||||
} else {
|
||||
put_buf(cstart, i - cstart, F, S);
|
||||
put_buf(cstart, i - cstart, F, S, T);
|
||||
}
|
||||
}
|
||||
if (unicode_escape) {
|
||||
@@ -98,27 +118,34 @@ static void jvp_dump_string(jv str, int ascii_only, FILE* F, jv* S) {
|
||||
sprintf(buf, "\\u%04x", c);
|
||||
} else {
|
||||
c -= 0x10000;
|
||||
sprintf(buf, "\\u%04x\\u%04x",
|
||||
sprintf(buf, "\\u%04x\\u%04x",
|
||||
0xD800 | ((c & 0xffc00) >> 10),
|
||||
0xDC00 | (c & 0x003ff));
|
||||
}
|
||||
put_str(buf, F, S);
|
||||
put_str(buf, F, S, T);
|
||||
}
|
||||
}
|
||||
assert(c != -1);
|
||||
put_char('"', F, S);
|
||||
put_char('"', F, S, T);
|
||||
}
|
||||
|
||||
enum { INDENT = 2 };
|
||||
static void put_refcnt(struct dtoa_context* C, int refcnt, FILE *F, jv* S, int T){
|
||||
char buf[JVP_DTOA_FMT_MAX_LEN];
|
||||
put_char(' ', F, S, T);
|
||||
put_char('(', F, S, T);
|
||||
put_str(jvp_dtoa_fmt(C, buf, refcnt), F, S, T);
|
||||
put_char(')', F, S, T);
|
||||
}
|
||||
|
||||
static void jv_dump_term(struct dtoa_context* C, jv x, int flags, int indent, FILE* F, jv* S) {
|
||||
char buf[JVP_DTOA_FMT_MAX_LEN];
|
||||
const char* colour = 0;
|
||||
double refcnt = (flags & JV_PRINT_REFCOUNT) ? jv_get_refcnt(x) - 1 : -1;
|
||||
if (flags & JV_PRINT_COLOUR) {
|
||||
for (unsigned i=0; i<sizeof(colour_kinds)/sizeof(colour_kinds[0]); i++) {
|
||||
if (jv_get_kind(x) == colour_kinds[i]) {
|
||||
colour = colours[i];
|
||||
put_str(colour, F, S);
|
||||
put_str(colour, F, S, flags & JV_PRINT_ISATTY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -126,119 +153,175 @@ static void jv_dump_term(struct dtoa_context* C, jv x, int flags, int indent, FI
|
||||
switch (jv_get_kind(x)) {
|
||||
default:
|
||||
case JV_KIND_INVALID:
|
||||
assert(0 && "Invalid value");
|
||||
if (flags & JV_PRINT_INVALID) {
|
||||
jv msg = jv_invalid_get_msg(jv_copy(x));
|
||||
if (jv_get_kind(msg) == JV_KIND_STRING) {
|
||||
put_str("<invalid:", F, S, flags & JV_PRINT_ISATTY);
|
||||
jvp_dump_string(msg, flags | JV_PRINT_ASCII, F, S, flags & JV_PRINT_ISATTY);
|
||||
put_str(">", F, S, flags & JV_PRINT_ISATTY);
|
||||
} else {
|
||||
put_str("<invalid>", F, S, flags & JV_PRINT_ISATTY);
|
||||
}
|
||||
} else {
|
||||
assert(0 && "Invalid value");
|
||||
}
|
||||
break;
|
||||
case JV_KIND_NULL:
|
||||
put_str("null", F, S);
|
||||
put_str("null", F, S, flags & JV_PRINT_ISATTY);
|
||||
break;
|
||||
case JV_KIND_FALSE:
|
||||
put_str("false", F, S);
|
||||
put_str("false", F, S, flags & JV_PRINT_ISATTY);
|
||||
break;
|
||||
case JV_KIND_TRUE:
|
||||
put_str("true", F, S);
|
||||
put_str("true", F, S, flags & JV_PRINT_ISATTY);
|
||||
break;
|
||||
case JV_KIND_NUMBER: {
|
||||
double d = jv_number_value(x);
|
||||
if (d != d) {
|
||||
// JSON doesn't have NaN, so we'll render it as "null"
|
||||
put_str("null", F, S);
|
||||
put_str("null", F, S, flags & JV_PRINT_ISATTY);
|
||||
} else {
|
||||
// Normalise infinities to something we can print in valid JSON
|
||||
if (d > DBL_MAX) d = DBL_MAX;
|
||||
if (d < -DBL_MAX) d = -DBL_MAX;
|
||||
put_str(jvp_dtoa_fmt(C, buf, d), F, S);
|
||||
put_str(jvp_dtoa_fmt(C, buf, d), F, S, flags & JV_PRINT_ISATTY);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JV_KIND_STRING:
|
||||
jvp_dump_string(x, flags & JV_PRINT_ASCII, F, S);
|
||||
jvp_dump_string(x, flags & JV_PRINT_ASCII, F, S, flags & JV_PRINT_ISATTY);
|
||||
if (flags & JV_PRINT_REFCOUNT)
|
||||
put_refcnt(C, refcnt, F, S, flags & JV_PRINT_ISATTY);
|
||||
break;
|
||||
case JV_KIND_ARRAY: {
|
||||
if (jv_array_length(jv_copy(x)) == 0) {
|
||||
put_str("[]", F, S);
|
||||
put_str("[]", F, S, flags & JV_PRINT_ISATTY);
|
||||
break;
|
||||
}
|
||||
put_str("[", F, S);
|
||||
put_str("[", F, S, flags & JV_PRINT_ISATTY);
|
||||
if (flags & JV_PRINT_PRETTY) {
|
||||
put_char('\n', F, S);
|
||||
put_space(indent+INDENT, F, S);
|
||||
put_char('\n', F, S, flags & JV_PRINT_ISATTY);
|
||||
put_indent(indent + 1, flags, F, S, flags & JV_PRINT_ISATTY);
|
||||
}
|
||||
jv_array_foreach(x, i, elem) {
|
||||
if (i!=0) {
|
||||
if (flags & JV_PRINT_PRETTY) {
|
||||
put_str(",\n", F, S);
|
||||
put_space(indent+INDENT, F, S);
|
||||
put_str(",\n", F, S, flags & JV_PRINT_ISATTY);
|
||||
put_indent(indent + 1, flags, F, S, flags & JV_PRINT_ISATTY);
|
||||
} else {
|
||||
put_str(",", F, S);
|
||||
put_str(",", F, S, flags & JV_PRINT_ISATTY);
|
||||
}
|
||||
}
|
||||
jv_dump_term(C, elem, flags, indent + INDENT, F, S);
|
||||
if (colour) put_str(colour, F, S);
|
||||
jv_dump_term(C, elem, flags, indent + 1, F, S);
|
||||
if (colour) put_str(colour, F, S, flags & JV_PRINT_ISATTY);
|
||||
}
|
||||
if (flags & JV_PRINT_PRETTY) {
|
||||
put_char('\n', F, S);
|
||||
put_space(indent, F, S);
|
||||
put_char('\n', F, S, flags & JV_PRINT_ISATTY);
|
||||
put_indent(indent, flags, F, S, flags & JV_PRINT_ISATTY);
|
||||
}
|
||||
if (colour) put_str(colour, F, S);
|
||||
put_char(']', F, S);
|
||||
if (colour) put_str(colour, F, S, flags & JV_PRINT_ISATTY);
|
||||
put_char(']', F, S, flags & JV_PRINT_ISATTY);
|
||||
if (flags & JV_PRINT_REFCOUNT)
|
||||
put_refcnt(C, refcnt, F, S, flags & JV_PRINT_ISATTY);
|
||||
break;
|
||||
}
|
||||
case JV_KIND_OBJECT: {
|
||||
if (jv_object_length(jv_copy(x)) == 0) {
|
||||
put_str("{}", F, S);
|
||||
put_str("{}", F, S, flags & JV_PRINT_ISATTY);
|
||||
break;
|
||||
}
|
||||
put_char('{', F, S);
|
||||
put_char('{', F, S, flags & JV_PRINT_ISATTY);
|
||||
if (flags & JV_PRINT_PRETTY) {
|
||||
put_char('\n', F, S);
|
||||
put_space(indent+INDENT, F, S);
|
||||
put_char('\n', F, S, flags & JV_PRINT_ISATTY);
|
||||
put_indent(indent + 1, flags, F, S, flags & JV_PRINT_ISATTY);
|
||||
}
|
||||
int first = 1;
|
||||
jv_object_foreach(x, key, value) {
|
||||
int i = 0;
|
||||
jv keyset = jv_null();
|
||||
while (1) {
|
||||
jv key, value;
|
||||
if (flags & JV_PRINT_SORTED) {
|
||||
if (first) {
|
||||
keyset = jv_keys(jv_copy(x));
|
||||
i = 0;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
if (i >= jv_array_length(jv_copy(keyset))) {
|
||||
jv_free(keyset);
|
||||
break;
|
||||
}
|
||||
key = jv_array_get(jv_copy(keyset), i);
|
||||
value = jv_object_get(jv_copy(x), jv_copy(key));
|
||||
} else {
|
||||
if (first) {
|
||||
i = jv_object_iter(x);
|
||||
} else {
|
||||
i = jv_object_iter_next(x, i);
|
||||
}
|
||||
if (!jv_object_iter_valid(x, i)) break;
|
||||
key = jv_object_iter_key(x, i);
|
||||
value = jv_object_iter_value(x, i);
|
||||
}
|
||||
|
||||
if (!first) {
|
||||
if (flags & JV_PRINT_PRETTY){
|
||||
put_str(",\n", F, S);
|
||||
put_space(indent+INDENT, F, S);
|
||||
put_str(",\n", F, S, flags & JV_PRINT_ISATTY);
|
||||
put_indent(indent + 1, flags, F, S, flags & JV_PRINT_ISATTY);
|
||||
} else {
|
||||
put_str(",", F, S);
|
||||
put_str(",", F, S, flags & JV_PRINT_ISATTY);
|
||||
}
|
||||
}
|
||||
if (colour) put_str(COLRESET, F, S);
|
||||
if (colour) put_str(COLRESET, F, S, flags & JV_PRINT_ISATTY);
|
||||
|
||||
first = 0;
|
||||
if (colour) put_str(FIELD_COLOUR, F, S);
|
||||
jvp_dump_string(key, flags & JV_PRINT_ASCII, F, S);
|
||||
if (colour) put_str(FIELD_COLOUR, F, S, flags & JV_PRINT_ISATTY);
|
||||
jvp_dump_string(key, flags & JV_PRINT_ASCII, F, S, flags & JV_PRINT_ISATTY);
|
||||
jv_free(key);
|
||||
if (colour) put_str(COLRESET, F, S);
|
||||
if (colour) put_str(COLRESET, F, S, flags & JV_PRINT_ISATTY);
|
||||
|
||||
if (colour) put_str(colour, F, S);
|
||||
put_str((flags & JV_PRINT_PRETTY) ? ": " : ":", F, S);
|
||||
if (colour) put_str(COLRESET, F, S);
|
||||
|
||||
jv_dump_term(C, value, flags, indent + INDENT, F, S);
|
||||
if (colour) put_str(colour, F, S);
|
||||
if (colour) put_str(colour, F, S, flags & JV_PRINT_ISATTY);
|
||||
put_str((flags & JV_PRINT_PRETTY) ? ": " : ":", F, S, flags & JV_PRINT_ISATTY);
|
||||
if (colour) put_str(COLRESET, F, S, flags & JV_PRINT_ISATTY);
|
||||
|
||||
jv_dump_term(C, value, flags, indent + 1, F, S);
|
||||
if (colour) put_str(colour, F, S, flags & JV_PRINT_ISATTY);
|
||||
}
|
||||
if (flags & JV_PRINT_PRETTY) {
|
||||
put_char('\n', F, S);
|
||||
put_space(indent, F, S);
|
||||
put_char('\n', F, S, flags & JV_PRINT_ISATTY);
|
||||
put_indent(indent, flags, F, S, flags & JV_PRINT_ISATTY);
|
||||
}
|
||||
if (colour) put_str(colour, F, S);
|
||||
put_char('}', F, S);
|
||||
if (colour) put_str(colour, F, S, flags & JV_PRINT_ISATTY);
|
||||
put_char('}', F, S, flags & JV_PRINT_ISATTY);
|
||||
if (flags & JV_PRINT_REFCOUNT)
|
||||
put_refcnt(C, refcnt, F, S, flags & JV_PRINT_ISATTY);
|
||||
}
|
||||
}
|
||||
jv_free(x);
|
||||
if (colour) {
|
||||
put_str(COLRESET, F, S);
|
||||
put_str(COLRESET, F, S, flags & JV_PRINT_ISATTY);
|
||||
}
|
||||
}
|
||||
|
||||
void jv_dump(jv x, int flags) {
|
||||
void jv_dumpf(jv x, FILE *f, int flags) {
|
||||
struct dtoa_context C;
|
||||
jvp_dtoa_context_init(&C);
|
||||
jv_dump_term(&C, x, flags, 0, stdout, 0);
|
||||
jv_dump_term(&C, x, flags, 0, f, 0);
|
||||
jvp_dtoa_context_free(&C);
|
||||
}
|
||||
|
||||
void jv_dump(jv x, int flags) {
|
||||
jv_dumpf(x, stdout, flags);
|
||||
}
|
||||
|
||||
/* This one is nice for use in debuggers */
|
||||
void jv_show(jv x, int flags) {
|
||||
if (flags == -1)
|
||||
flags = JV_PRINT_PRETTY | JV_PRINT_COLOUR | JV_PRINT_INDENT_FLAGS(2);
|
||||
jv_dumpf(jv_copy(x), stderr, flags | JV_PRINT_INVALID);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
jv jv_dump_string(jv x, int flags) {
|
||||
struct dtoa_context C;
|
||||
jvp_dtoa_context_init(&C);
|
||||
@@ -247,3 +330,19 @@ jv jv_dump_string(jv x, int flags) {
|
||||
jvp_dtoa_context_free(&C);
|
||||
return s;
|
||||
}
|
||||
|
||||
char *jv_dump_string_trunc(jv x, char *outbuf, size_t bufsize) {
|
||||
x = jv_dump_string(x,0);
|
||||
const char* p = jv_string_value(x);
|
||||
const size_t len = strlen(p);
|
||||
strncpy(outbuf, p, bufsize);
|
||||
outbuf[bufsize - 1] = 0;
|
||||
if (len > bufsize - 1 && bufsize >= 4) {
|
||||
// Indicate truncation with '...'
|
||||
outbuf[bufsize - 2]='.';
|
||||
outbuf[bufsize - 3]='.';
|
||||
outbuf[bufsize - 4]='.';
|
||||
}
|
||||
jv_free(x);
|
||||
return outbuf;
|
||||
}
|
||||
+48
-16
@@ -3,35 +3,67 @@
|
||||
#include "jv_unicode.h"
|
||||
#include "jv_utf8_tables.h"
|
||||
|
||||
const char* jvp_utf8_next(const char* in, const char* end, int* codepoint) {
|
||||
const char* jvp_utf8_next(const char* in, const char* end, int* codepoint_ret) {
|
||||
assert(in <= end);
|
||||
if (in == end) {
|
||||
codepoint = 0;
|
||||
return 0;
|
||||
}
|
||||
int codepoint = -1;
|
||||
unsigned char first = (unsigned char)in[0];
|
||||
int length = utf8_coding_length[first];
|
||||
if (length == 0 || length == UTF8_CONTINUATION_BYTE || in + length > end) {
|
||||
*codepoint = -1;
|
||||
return 0;
|
||||
}
|
||||
*codepoint = ((unsigned)in[0]) & utf8_coding_bits[first];
|
||||
for (int i=1; i<length; i++) {
|
||||
int ch = (unsigned char)in[i];
|
||||
if (utf8_coding_length[(unsigned char)in[i]] != UTF8_CONTINUATION_BYTE){
|
||||
*codepoint = -1;
|
||||
return 0;
|
||||
if ((first & 0x80) == 0) {
|
||||
/* Fast-path for ASCII */
|
||||
codepoint = first;
|
||||
length = 1;
|
||||
} else if (length == 0 || length == UTF8_CONTINUATION_BYTE) {
|
||||
/* Bad single byte - either an invalid byte or an out-of-place continuation byte */
|
||||
length = 1;
|
||||
} else if (in + length > end) {
|
||||
/* String ends before UTF8 sequence ends */
|
||||
length = end - in;
|
||||
} else {
|
||||
codepoint = ((unsigned)in[0]) & utf8_coding_bits[first];
|
||||
for (int i=1; i<length; i++) {
|
||||
unsigned ch = (unsigned char)in[i];
|
||||
if (utf8_coding_length[ch] != UTF8_CONTINUATION_BYTE){
|
||||
/* Invalid UTF8 sequence - not followed by the right number of continuation bytes */
|
||||
codepoint = -1;
|
||||
length = i;
|
||||
break;
|
||||
}
|
||||
codepoint = (codepoint << 6) | (ch & 0x3f);
|
||||
}
|
||||
if (codepoint < utf8_first_codepoint[length]) {
|
||||
/* Overlong UTF8 sequence */
|
||||
codepoint = -1;
|
||||
}
|
||||
if (0xD800 <= codepoint && codepoint <= 0xDFFF) {
|
||||
/* Surrogate codepoints can't be encoded in UTF8 */
|
||||
codepoint = -1;
|
||||
}
|
||||
if (codepoint > 0x10FFFF) {
|
||||
/* Outside Unicode range */
|
||||
codepoint = -1;
|
||||
}
|
||||
*codepoint = (*codepoint << 6) | (ch & 0x3f);
|
||||
}
|
||||
assert(length > 0);
|
||||
*codepoint_ret = codepoint;
|
||||
return in + length;
|
||||
}
|
||||
|
||||
int jvp_utf8_verify(const char* in, const char* end) {
|
||||
int codepoint = 0;
|
||||
int jvp_utf8_is_valid(const char* in, const char* end) {
|
||||
int codepoint;
|
||||
while ((in = jvp_utf8_next(in, end, &codepoint))) {
|
||||
if (codepoint == -1) return 0;
|
||||
}
|
||||
return codepoint != -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int jvp_utf8_decode_length(char startchar) {
|
||||
if ((startchar & 0x80) == 0) return 1;
|
||||
else if ((startchar & 0xC0) == 0xC0) return 2;
|
||||
else if ((startchar & 0xE0) == 0xE0) return 3;
|
||||
else return 4;
|
||||
}
|
||||
|
||||
int jvp_utf8_encode_length(int codepoint) {
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
#define JV_UNICODE_H
|
||||
|
||||
const char* jvp_utf8_next(const char* in, const char* end, int* codepoint);
|
||||
|
||||
int jvp_utf8_is_valid(const char* in, const char* end);
|
||||
|
||||
int jvp_utf8_decode_length(char startchar);
|
||||
|
||||
|
||||
@@ -33,3 +33,5 @@ static const unsigned char utf8_coding_bits[] =
|
||||
0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
|
||||
0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
|
||||
0x07, 0x07, 0x07, 0x07, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
static const int utf8_first_codepoint[] =
|
||||
{0x00, 0x00, 0x80, 0x800, 0x10000};
|
||||
@@ -0,0 +1,362 @@
|
||||
#ifndef jq_yyHEADER_H
|
||||
#define jq_yyHEADER_H 1
|
||||
#define jq_yyIN_HEADER 1
|
||||
|
||||
#line 6 "lexer.h"
|
||||
|
||||
#line 8 "lexer.h"
|
||||
|
||||
#define YY_INT_ALIGNED short int
|
||||
|
||||
/* A lexical scanner generated by flex */
|
||||
|
||||
#define FLEX_SCANNER
|
||||
#define YY_FLEX_MAJOR_VERSION 2
|
||||
#define YY_FLEX_MINOR_VERSION 5
|
||||
#define YY_FLEX_SUBMINOR_VERSION 35
|
||||
#if YY_FLEX_SUBMINOR_VERSION > 0
|
||||
#define FLEX_BETA
|
||||
#endif
|
||||
|
||||
/* First, we deal with platform-specific or compiler-specific issues. */
|
||||
|
||||
/* begin standard C headers. */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* end standard C headers. */
|
||||
|
||||
/* flex integer type definitions */
|
||||
|
||||
#ifndef FLEXINT_H
|
||||
#define FLEXINT_H
|
||||
|
||||
/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
|
||||
|
||||
#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
|
||||
|
||||
/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
|
||||
* if you want the limit (max/min) macros for int types.
|
||||
*/
|
||||
#ifndef __STDC_LIMIT_MACROS
|
||||
#define __STDC_LIMIT_MACROS 1
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
typedef int8_t flex_int8_t;
|
||||
typedef uint8_t flex_uint8_t;
|
||||
typedef int16_t flex_int16_t;
|
||||
typedef uint16_t flex_uint16_t;
|
||||
typedef int32_t flex_int32_t;
|
||||
typedef uint32_t flex_uint32_t;
|
||||
#else
|
||||
typedef signed char flex_int8_t;
|
||||
typedef short int flex_int16_t;
|
||||
typedef int flex_int32_t;
|
||||
typedef unsigned char flex_uint8_t;
|
||||
typedef unsigned short int flex_uint16_t;
|
||||
typedef unsigned int flex_uint32_t;
|
||||
|
||||
/* Limits of integral types. */
|
||||
#ifndef INT8_MIN
|
||||
#define INT8_MIN (-128)
|
||||
#endif
|
||||
#ifndef INT16_MIN
|
||||
#define INT16_MIN (-32767-1)
|
||||
#endif
|
||||
#ifndef INT32_MIN
|
||||
#define INT32_MIN (-2147483647-1)
|
||||
#endif
|
||||
#ifndef INT8_MAX
|
||||
#define INT8_MAX (127)
|
||||
#endif
|
||||
#ifndef INT16_MAX
|
||||
#define INT16_MAX (32767)
|
||||
#endif
|
||||
#ifndef INT32_MAX
|
||||
#define INT32_MAX (2147483647)
|
||||
#endif
|
||||
#ifndef UINT8_MAX
|
||||
#define UINT8_MAX (255U)
|
||||
#endif
|
||||
#ifndef UINT16_MAX
|
||||
#define UINT16_MAX (65535U)
|
||||
#endif
|
||||
#ifndef UINT32_MAX
|
||||
#define UINT32_MAX (4294967295U)
|
||||
#endif
|
||||
|
||||
#endif /* ! C99 */
|
||||
|
||||
#endif /* ! FLEXINT_H */
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
/* The "const" storage-class-modifier is valid. */
|
||||
#define YY_USE_CONST
|
||||
|
||||
#else /* ! __cplusplus */
|
||||
|
||||
/* C99 requires __STDC__ to be defined as 1. */
|
||||
#if defined (__STDC__)
|
||||
|
||||
#define YY_USE_CONST
|
||||
|
||||
#endif /* defined (__STDC__) */
|
||||
#endif /* ! __cplusplus */
|
||||
|
||||
#ifdef YY_USE_CONST
|
||||
#define yyconst const
|
||||
#else
|
||||
#define yyconst
|
||||
#endif
|
||||
|
||||
/* An opaque pointer. */
|
||||
#ifndef YY_TYPEDEF_YY_SCANNER_T
|
||||
#define YY_TYPEDEF_YY_SCANNER_T
|
||||
typedef void* yyscan_t;
|
||||
#endif
|
||||
|
||||
/* For convenience, these vars (plus the bison vars far below)
|
||||
are macros in the reentrant scanner. */
|
||||
#define yyin yyg->yyin_r
|
||||
#define yyout yyg->yyout_r
|
||||
#define yyextra yyg->yyextra_r
|
||||
#define yyleng yyg->yyleng_r
|
||||
#define yytext yyg->yytext_r
|
||||
#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
|
||||
#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
|
||||
#define yy_flex_debug yyg->yy_flex_debug_r
|
||||
|
||||
/* Size of default input buffer. */
|
||||
#ifndef YY_BUF_SIZE
|
||||
#ifdef __ia64__
|
||||
/* On IA-64, the buffer size is 16k, not 8k.
|
||||
* Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
|
||||
* Ditto for the __ia64__ case accordingly.
|
||||
*/
|
||||
#define YY_BUF_SIZE 32768
|
||||
#else
|
||||
#define YY_BUF_SIZE 16384
|
||||
#endif /* __ia64__ */
|
||||
#endif
|
||||
|
||||
#ifndef YY_TYPEDEF_YY_BUFFER_STATE
|
||||
#define YY_TYPEDEF_YY_BUFFER_STATE
|
||||
typedef struct yy_buffer_state *YY_BUFFER_STATE;
|
||||
#endif
|
||||
|
||||
#ifndef YY_TYPEDEF_YY_SIZE_T
|
||||
#define YY_TYPEDEF_YY_SIZE_T
|
||||
typedef size_t yy_size_t;
|
||||
#endif
|
||||
|
||||
#ifndef YY_STRUCT_YY_BUFFER_STATE
|
||||
#define YY_STRUCT_YY_BUFFER_STATE
|
||||
struct yy_buffer_state
|
||||
{
|
||||
FILE *yy_input_file;
|
||||
|
||||
char *yy_ch_buf; /* input buffer */
|
||||
char *yy_buf_pos; /* current position in input buffer */
|
||||
|
||||
/* Size of input buffer in bytes, not including room for EOB
|
||||
* characters.
|
||||
*/
|
||||
yy_size_t yy_buf_size;
|
||||
|
||||
/* Number of characters read into yy_ch_buf, not including EOB
|
||||
* characters.
|
||||
*/
|
||||
int yy_n_chars;
|
||||
|
||||
/* Whether we "own" the buffer - i.e., we know we created it,
|
||||
* and can realloc() it to grow it, and should free() it to
|
||||
* delete it.
|
||||
*/
|
||||
int yy_is_our_buffer;
|
||||
|
||||
/* Whether this is an "interactive" input source; if so, and
|
||||
* if we're using stdio for input, then we want to use getc()
|
||||
* instead of fread(), to make sure we stop fetching input after
|
||||
* each newline.
|
||||
*/
|
||||
int yy_is_interactive;
|
||||
|
||||
/* Whether we're considered to be at the beginning of a line.
|
||||
* If so, '^' rules will be active on the next match, otherwise
|
||||
* not.
|
||||
*/
|
||||
int yy_at_bol;
|
||||
|
||||
int yy_bs_lineno; /**< The line count. */
|
||||
int yy_bs_column; /**< The column count. */
|
||||
|
||||
/* Whether to try to fill the input buffer when we reach the
|
||||
* end of it.
|
||||
*/
|
||||
int yy_fill_buffer;
|
||||
|
||||
int yy_buffer_status;
|
||||
|
||||
};
|
||||
#endif /* !YY_STRUCT_YY_BUFFER_STATE */
|
||||
|
||||
void jq_yyrestart (FILE *input_file ,yyscan_t yyscanner );
|
||||
void jq_yy_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
|
||||
YY_BUFFER_STATE jq_yy_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
|
||||
void jq_yy_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
|
||||
void jq_yy_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
|
||||
void jq_yypush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
|
||||
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 );
|
||||
|
||||
void *jq_yyalloc (yy_size_t ,yyscan_t yyscanner );
|
||||
void *jq_yyrealloc (void *,yy_size_t ,yyscan_t yyscanner );
|
||||
void jq_yyfree (void * ,yyscan_t yyscanner );
|
||||
|
||||
/* Begin user sect3 */
|
||||
|
||||
#define jq_yywrap(n) 1
|
||||
#define YY_SKIP_YYWRAP
|
||||
|
||||
#define yytext_ptr yytext_r
|
||||
|
||||
#ifdef YY_HEADER_EXPORT_START_CONDITIONS
|
||||
#define INITIAL 0
|
||||
#define IN_PAREN 1
|
||||
#define IN_BRACKET 2
|
||||
#define IN_BRACE 3
|
||||
#define IN_QQINTERP 4
|
||||
#define IN_QQSTRING 5
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef YY_NO_UNISTD_H
|
||||
/* Special case for "unistd.h", since it is non-ANSI. We include it way
|
||||
* down here because we want the user's section 1 to have been scanned first.
|
||||
* The user has a chance to override it with an option.
|
||||
*/
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#define YY_EXTRA_TYPE int
|
||||
|
||||
int jq_yylex_init (yyscan_t* scanner);
|
||||
|
||||
int jq_yylex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);
|
||||
|
||||
/* Accessor methods to globals.
|
||||
These are made visible to non-reentrant scanners for convenience. */
|
||||
|
||||
int jq_yylex_destroy (yyscan_t yyscanner );
|
||||
|
||||
int jq_yyget_debug (yyscan_t yyscanner );
|
||||
|
||||
void jq_yyset_debug (int debug_flag ,yyscan_t yyscanner );
|
||||
|
||||
YY_EXTRA_TYPE jq_yyget_extra (yyscan_t yyscanner );
|
||||
|
||||
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 );
|
||||
|
||||
FILE *jq_yyget_out (yyscan_t yyscanner );
|
||||
|
||||
void jq_yyset_out (FILE * out_str ,yyscan_t yyscanner );
|
||||
|
||||
int 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 );
|
||||
|
||||
YYSTYPE * jq_yyget_lval (yyscan_t yyscanner );
|
||||
|
||||
void jq_yyset_lval (YYSTYPE * yylval_param ,yyscan_t yyscanner );
|
||||
|
||||
YYLTYPE *jq_yyget_lloc (yyscan_t yyscanner );
|
||||
|
||||
void jq_yyset_lloc (YYLTYPE * yylloc_param ,yyscan_t yyscanner );
|
||||
|
||||
/* Macros after this point can all be overridden by user definitions in
|
||||
* section 1.
|
||||
*/
|
||||
|
||||
#ifndef YY_SKIP_YYWRAP
|
||||
#ifdef __cplusplus
|
||||
extern "C" int jq_yywrap (yyscan_t yyscanner );
|
||||
#else
|
||||
extern int jq_yywrap (yyscan_t yyscanner );
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef yytext_ptr
|
||||
static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
|
||||
#endif
|
||||
|
||||
#ifdef YY_NEED_STRLEN
|
||||
static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
|
||||
#endif
|
||||
|
||||
#ifndef YY_NO_INPUT
|
||||
|
||||
#endif
|
||||
|
||||
/* Amount of stuff to slurp up with each read. */
|
||||
#ifndef YY_READ_BUF_SIZE
|
||||
#ifdef __ia64__
|
||||
/* On IA-64, the buffer size is 16k, not 8k */
|
||||
#define YY_READ_BUF_SIZE 16384
|
||||
#else
|
||||
#define YY_READ_BUF_SIZE 8192
|
||||
#endif /* __ia64__ */
|
||||
#endif
|
||||
|
||||
/* Number of entries by which start-condition stack grows. */
|
||||
#ifndef YY_START_STACK_INCR
|
||||
#define YY_START_STACK_INCR 25
|
||||
#endif
|
||||
|
||||
/* Default declaration of generated scanner - a define so the user can
|
||||
* easily add parameters.
|
||||
*/
|
||||
#ifndef YY_DECL
|
||||
#define YY_DECL_IS_OURS 1
|
||||
|
||||
extern int jq_yylex \
|
||||
(YYSTYPE * yylval_param,YYLTYPE * yylloc_param ,yyscan_t yyscanner);
|
||||
|
||||
#define YY_DECL int jq_yylex \
|
||||
(YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner)
|
||||
#endif /* !YY_DECL */
|
||||
|
||||
/* yy_get_previous_state - get the state just before the EOB char was reached */
|
||||
|
||||
#undef YY_NEW_FILE
|
||||
#undef YY_FLUSH_BUFFER
|
||||
#undef yy_set_bol
|
||||
#undef yy_new_buffer
|
||||
#undef yy_set_interactive
|
||||
#undef YY_DO_BEFORE_ACTION
|
||||
|
||||
#ifdef YY_DECL_IS_OURS
|
||||
#undef YY_DECL_IS_OURS
|
||||
#undef YY_DECL
|
||||
#endif
|
||||
|
||||
#line 129 "lexer.l"
|
||||
|
||||
|
||||
#line 361 "lexer.h"
|
||||
#undef jq_yyIN_HEADER
|
||||
#endif /* jq_yyHEADER_H */
|
||||
@@ -1,4 +1,5 @@
|
||||
%{
|
||||
#include <assert.h>
|
||||
#include "jv_alloc.h"
|
||||
#include "compile.h"
|
||||
|
||||
@@ -41,6 +42,9 @@ struct lexer_param;
|
||||
"!=" { return NEQ; }
|
||||
"==" { return EQ; }
|
||||
"as" { return AS; }
|
||||
"import" { return IMPORT; }
|
||||
"include" { return INCLUDE; }
|
||||
"module" { return MODULE; }
|
||||
"def" { return DEF; }
|
||||
"if" { return IF; }
|
||||
"then" { return THEN; }
|
||||
@@ -50,16 +54,24 @@ struct lexer_param;
|
||||
"or" { return OR; }
|
||||
"end" { return END; }
|
||||
"reduce" { return REDUCE; }
|
||||
"foreach" { return FOREACH; }
|
||||
"//" { return DEFINEDOR; }
|
||||
"try" { return TRY; }
|
||||
"catch" { return CATCH; }
|
||||
"label" { return LABEL; }
|
||||
"break" { return BREAK; }
|
||||
"__loc__" { return LOC; }
|
||||
"|=" { return SETPIPE; }
|
||||
"+=" { return SETPLUS; }
|
||||
"-=" { return SETMINUS; }
|
||||
"*=" { return SETMULT; }
|
||||
"/=" { return SETDIV; }
|
||||
"%=" { return SETMOD; }
|
||||
"//=" { return SETDEFINEDOR; }
|
||||
"<=" { return LESSEQ; }
|
||||
">=" { return GREATEREQ; }
|
||||
"."|"="|";"|","|":"|"|"|"+"|"-"|"*"|"/"|"\$"|"<"|">" { return yytext[0];}
|
||||
".." { return REC; }
|
||||
"."|"?"|"="|";"|","|":"|"|"|"+"|"-"|"*"|"/"|"%"|"\$"|"<"|">" { return yytext[0];}
|
||||
|
||||
"["|"{"|"(" {
|
||||
return enter(yytext[0], YY_START, yyscanner);
|
||||
@@ -92,7 +104,7 @@ struct lexer_param;
|
||||
}
|
||||
(\\[^u(]|\\u[a-zA-Z0-9]{0,4})+ {
|
||||
/* pass escapes to the json parser */
|
||||
jv escapes = jv_string_fmt("\"%.*s\"", yyleng, yytext);
|
||||
jv escapes = jv_string_fmt("\"%.*s\"", (int)yyleng, yytext);
|
||||
yylval->literal = jv_parse_sized(jv_string_value(escapes), jv_string_length_bytes(jv_copy(escapes)));
|
||||
jv_free(escapes);
|
||||
return QQSTRING_TEXT;
|
||||
@@ -107,7 +119,8 @@ struct lexer_param;
|
||||
}
|
||||
|
||||
|
||||
[a-zA-Z_][a-zA-Z_0-9]* { yylval->literal = jv_string(yytext); return IDENT;}
|
||||
([a-zA-Z_][a-zA-Z_0-9]*::)*[a-zA-Z_][a-zA-Z_0-9]* { yylval->literal = jv_string(yytext); return IDENT;}
|
||||
\.[a-zA-Z_][a-zA-Z_0-9]* { yylval->literal = jv_string(yytext+1); return FIELD;}
|
||||
|
||||
[ \n\t]+ {}
|
||||
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
#ifdef HAVE_ACOS
|
||||
LIBM_DD(acos)
|
||||
#else
|
||||
LIBM_DD_NO(acos)
|
||||
#endif
|
||||
#ifdef HAVE_ACOSH
|
||||
LIBM_DD(acosh)
|
||||
#else
|
||||
LIBM_DD_NO(acosh)
|
||||
#endif
|
||||
#ifdef HAVE_ASIN
|
||||
LIBM_DD(asin)
|
||||
#else
|
||||
LIBM_DD_NO(asin)
|
||||
#endif
|
||||
#ifdef HAVE_ASINH
|
||||
LIBM_DD(asinh)
|
||||
#else
|
||||
LIBM_DD_NO(asinh)
|
||||
#endif
|
||||
#ifdef HAVE_ATAN
|
||||
LIBM_DD(atan)
|
||||
#else
|
||||
LIBM_DD_NO(atan)
|
||||
#endif
|
||||
#ifdef HAVE_ATANH
|
||||
LIBM_DD(atanh)
|
||||
#else
|
||||
LIBM_DD_NO(atanh)
|
||||
#endif
|
||||
#ifdef HAVE_CBRT
|
||||
LIBM_DD(cbrt)
|
||||
#else
|
||||
LIBM_DD_NO(cbrt)
|
||||
#endif
|
||||
#ifdef HAVE_COS
|
||||
LIBM_DD(cos)
|
||||
#else
|
||||
LIBM_DD_NO(cos)
|
||||
#endif
|
||||
#ifdef HAVE_COSH
|
||||
LIBM_DD(cosh)
|
||||
#else
|
||||
LIBM_DD_NO(cosh)
|
||||
#endif
|
||||
#ifdef HAVE_EXP2
|
||||
LIBM_DD(exp2)
|
||||
#else
|
||||
LIBM_DD_NO(exp2)
|
||||
#endif
|
||||
#ifdef HAVE_EXP
|
||||
LIBM_DD(exp)
|
||||
#else
|
||||
LIBM_DD_NO(exp)
|
||||
#endif
|
||||
#ifdef HAVE_FLOOR
|
||||
LIBM_DD(floor)
|
||||
#else
|
||||
LIBM_DD_NO(floor)
|
||||
#endif
|
||||
#ifdef HAVE_J0
|
||||
LIBM_DD(j0)
|
||||
#else
|
||||
LIBM_DD_NO(j0)
|
||||
#endif
|
||||
#ifdef HAVE_J1
|
||||
LIBM_DD(j1)
|
||||
#else
|
||||
LIBM_DD_NO(j1)
|
||||
#endif
|
||||
#ifdef HAVE_LOG10
|
||||
LIBM_DD(log10)
|
||||
#else
|
||||
LIBM_DD_NO(log10)
|
||||
#endif
|
||||
#ifdef HAVE_LOG2
|
||||
LIBM_DD(log2)
|
||||
#else
|
||||
LIBM_DD_NO(log2)
|
||||
#endif
|
||||
#ifdef HAVE_LOG
|
||||
LIBM_DD(log)
|
||||
#else
|
||||
LIBM_DD_NO(log)
|
||||
#endif
|
||||
#ifdef HAVE_SIN
|
||||
LIBM_DD(sin)
|
||||
#else
|
||||
LIBM_DD_NO(sin)
|
||||
#endif
|
||||
#ifdef HAVE_SINH
|
||||
LIBM_DD(sinh)
|
||||
#else
|
||||
LIBM_DD_NO(sinh)
|
||||
#endif
|
||||
#ifdef HAVE_SQRT
|
||||
LIBM_DD(sqrt)
|
||||
#else
|
||||
LIBM_DD_NO(sqrt)
|
||||
#endif
|
||||
#ifdef HAVE_TAN
|
||||
LIBM_DD(tan)
|
||||
#else
|
||||
LIBM_DD_NO(tan)
|
||||
#endif
|
||||
#ifdef HAVE_TANH
|
||||
LIBM_DD(tanh)
|
||||
#else
|
||||
LIBM_DD_NO(tanh)
|
||||
#endif
|
||||
#ifdef HAVE_TGAMMA
|
||||
LIBM_DD(tgamma)
|
||||
#else
|
||||
LIBM_DD_NO(tgamma)
|
||||
#endif
|
||||
#ifdef HAVE_Y0
|
||||
LIBM_DD(y0)
|
||||
#else
|
||||
LIBM_DD_NO(y0)
|
||||
#endif
|
||||
#ifdef HAVE_Y1
|
||||
LIBM_DD(y1)
|
||||
#else
|
||||
LIBM_DD_NO(y1)
|
||||
#endif
|
||||
#ifdef HAVE_POW
|
||||
LIBM_DDD(pow)
|
||||
#else
|
||||
LIBM_DDD_NO(pow)
|
||||
#endif
|
||||
#ifdef HAVE_JN
|
||||
LIBM_DID(jn)
|
||||
#endif
|
||||
#ifdef HAVE_YN
|
||||
LIBM_DID(yn)
|
||||
#endif
|
||||
#ifdef HAVE_ATAN2
|
||||
LIBM_DDD(atan2)
|
||||
#else
|
||||
LIBM_DDD_NO(atan2)
|
||||
#endif
|
||||
#ifdef HAVE_HYPOT
|
||||
LIBM_DDD(hypot)
|
||||
#else
|
||||
LIBM_DDD_NO(hypot)
|
||||
#endif
|
||||
#ifdef HAVE_REMAINDER
|
||||
LIBM_DDD(remainder)
|
||||
#else
|
||||
LIBM_DDD_NO(remainder)
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#ifdef HAVE_SCALBN
|
||||
LIBM_DDI(scalbn)
|
||||
#endif
|
||||
#ifdef HAVE_LGAMMA_R
|
||||
LIBM_DDIP(lgamma_r)
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,358 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/stat.h>
|
||||
#include <libgen.h>
|
||||
|
||||
#include "jq_parser.h"
|
||||
#include "locfile.h"
|
||||
#include "jv.h"
|
||||
#include "jq.h"
|
||||
#include "parser.h"
|
||||
#include "util.h"
|
||||
#include "compile.h"
|
||||
|
||||
struct lib_loading_state {
|
||||
char **names;
|
||||
block *defs;
|
||||
uint64_t ct;
|
||||
};
|
||||
static int load_library(jq_state *jq, jv lib_path, int is_data, int raw,
|
||||
const char *as, block *out_block,
|
||||
struct lib_loading_state *lib_state);
|
||||
|
||||
|
||||
// Given a lib_path to search first, creates a chain of search paths
|
||||
// in the following order:
|
||||
// 1. lib_path
|
||||
// 2. -L paths passed in on the command line (from jq_state*) or builtin list
|
||||
static jv build_lib_search_chain(jq_state *jq, jv search_path, jv jq_origin, jv lib_origin) {
|
||||
assert(jv_get_kind(search_path) == JV_KIND_ARRAY);
|
||||
jv expanded = jv_array();
|
||||
jv expanded_elt;
|
||||
jv_array_foreach(search_path, i, path) {
|
||||
if (strcmp(".",jv_string_value(path)) == 0) {
|
||||
expanded_elt = jv_copy(path);
|
||||
} else if (jv_get_kind(lib_origin) == JV_KIND_STRING &&
|
||||
strncmp("./", jv_string_value(path),sizeof("./")-1) == 0) {
|
||||
expanded_elt = jv_string_fmt("%s/%s",
|
||||
jv_string_value(lib_origin),
|
||||
jv_string_value(path) + sizeof ("./") - 1);
|
||||
} else if (strncmp("$ORIGIN/",jv_string_value(path),sizeof("$ORIGIN/")-1) == 0) {
|
||||
expanded_elt = jv_string_fmt("%s/%s",
|
||||
jv_string_value(jq_origin),
|
||||
jv_string_value(path) + sizeof ("$ORIGIN/") - 1);
|
||||
} else {
|
||||
expanded_elt = expand_path(path);
|
||||
if (!jv_is_valid(expanded_elt)) {
|
||||
jv_free(search_path);
|
||||
jv_free(expanded);
|
||||
jv_free(path);
|
||||
return expanded_elt;
|
||||
}
|
||||
path = jv_invalid();
|
||||
}
|
||||
expanded = jv_array_append(expanded, expanded_elt);
|
||||
jv_free(path);
|
||||
}
|
||||
jv_free(jq_origin);
|
||||
jv_free(lib_origin);
|
||||
jv_free(search_path);
|
||||
return expanded;
|
||||
}
|
||||
|
||||
// Doesn't actually check that name not be an absolute path, and we
|
||||
// don't have to: we always append relative paths to others (with a '/'
|
||||
// in between).
|
||||
static jv validate_relpath(jv name) {
|
||||
const char *s = jv_string_value(name);
|
||||
if (strchr(s, '\\')) {
|
||||
jv res = jv_invalid_with_msg(jv_string_fmt("Modules must be named by relative paths using '/', not '\\' (%s)", s));
|
||||
jv_free(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);
|
||||
return res;
|
||||
}
|
||||
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(components);
|
||||
jv_free(name);
|
||||
return rp;
|
||||
}
|
||||
|
||||
// Assumes name has been validated
|
||||
static jv jv_basename(jv name) {
|
||||
const char *s = jv_string_value(name);
|
||||
const char *p = strrchr(s, '/');
|
||||
if (!p)
|
||||
return name;
|
||||
jv res = jv_string_fmt("%s", p);
|
||||
jv_free(name);
|
||||
return res;
|
||||
}
|
||||
|
||||
// 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)
|
||||
return jv_invalid_with_msg(jv_string_fmt("Module path must be a string"));
|
||||
|
||||
struct stat st;
|
||||
int ret;
|
||||
|
||||
// Ideally we should cache this somewhere
|
||||
search = build_lib_search_chain(jq, search, jq_origin, lib_origin);
|
||||
|
||||
jv bname = jv_basename(jv_copy(rel_path));
|
||||
|
||||
jv_array_foreach(search, i, spath) {
|
||||
if (jv_get_kind(spath) == JV_KIND_NULL) {
|
||||
jv_free(spath);
|
||||
break;
|
||||
}
|
||||
if (jv_get_kind(spath) != JV_KIND_STRING ||
|
||||
strcmp(jv_string_value(spath), "") == 0) {
|
||||
jv_free(spath);
|
||||
continue; /* XXX report non-strings in search path?? */
|
||||
}
|
||||
// Try ${search_dir}/${rel_path}.jq
|
||||
jv testpath = jq_realpath(jv_string_fmt("%s/%s%s",
|
||||
jv_string_value(spath),
|
||||
jv_string_value(rel_path),
|
||||
suffix));
|
||||
ret = stat(jv_string_value(testpath),&st);
|
||||
if (ret == -1 && errno == ENOENT) {
|
||||
jv_free(testpath);
|
||||
// Try ${search_dir}/$(dirname ${rel_path})/jq/main.jq
|
||||
testpath = jq_realpath(jv_string_fmt("%s/%s/%s%s",
|
||||
jv_string_value(spath),
|
||||
jv_string_value(rel_path),
|
||||
"jq/main",
|
||||
suffix));
|
||||
ret = stat(jv_string_value(testpath),&st);
|
||||
}
|
||||
if (ret == -1 && errno == ENOENT) {
|
||||
jv_free(testpath);
|
||||
// Try ${search_dir}/${rel_path}/$(basename ${rel_path}).jq
|
||||
testpath = jq_realpath(jv_string_fmt("%s/%s/%s%s",
|
||||
jv_string_value(spath),
|
||||
jv_string_value(rel_path),
|
||||
jv_string_value(bname),
|
||||
suffix));
|
||||
ret = stat(jv_string_value(testpath),&st);
|
||||
}
|
||||
if (ret == 0) {
|
||||
jv_free(rel_path);
|
||||
jv_free(search);
|
||||
jv_free(bname);
|
||||
jv_free(spath);
|
||||
return testpath;
|
||||
}
|
||||
jv_free(testpath);
|
||||
jv_free(spath);
|
||||
}
|
||||
jv output = jv_invalid_with_msg(jv_string_fmt("module not found: %s", jv_string_value(rel_path)));
|
||||
jv_free(rel_path);
|
||||
jv_free(search);
|
||||
jv_free(bname);
|
||||
return output;
|
||||
}
|
||||
|
||||
static jv default_search(jq_state *jq, jv value) {
|
||||
if (!jv_is_valid(value)) {
|
||||
// dependent didn't say; prepend . to system search path listj
|
||||
jv_free(value);
|
||||
return jv_array_concat(JV_ARRAY(jv_string(".")), jq_get_lib_dirs(jq));
|
||||
}
|
||||
if (jv_get_kind(value) != JV_KIND_ARRAY)
|
||||
return JV_ARRAY(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
// XXX Split this into a util that takes a callback, and then...
|
||||
static int process_dependencies(jq_state *jq, jv jq_origin, jv lib_origin, block *src_block, struct lib_loading_state *lib_state) {
|
||||
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) {
|
||||
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;
|
||||
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"));
|
||||
assert(!jv_is_valid(as) || jv_get_kind(as) == JV_KIND_STRING);
|
||||
if (jv_get_kind(as) == JV_KIND_STRING)
|
||||
as_str = jv_string_value(as);
|
||||
jv search = default_search(jq, jv_object_get(dep, jv_string("search")));
|
||||
// dep is now freed; do not reuse
|
||||
|
||||
// find_lib does a lot of work that could be cached...
|
||||
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 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();
|
||||
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);
|
||||
jv_free(jq_origin);
|
||||
jv_free(deps);
|
||||
return nerrors;
|
||||
}
|
||||
|
||||
// Loads the library at lib_path into lib_state, putting the library's defs
|
||||
// into *out_block
|
||||
static int load_library(jq_state *jq, jv lib_path, int is_data, int raw, const char *as, block *out_block, struct lib_loading_state *lib_state) {
|
||||
int nerrors = 0;
|
||||
struct locfile* src = NULL;
|
||||
block program;
|
||||
jv data;
|
||||
if (is_data && !raw)
|
||||
data = jv_load_file(jv_string_value(lib_path), 0);
|
||||
else
|
||||
data = jv_load_file(jv_string_value(lib_path), 1);
|
||||
int state_idx;
|
||||
if (!jv_is_valid(data)) {
|
||||
if (jv_invalid_has_msg(jv_copy(data)))
|
||||
data = jv_invalid_get_msg(data);
|
||||
else
|
||||
data = jv_string("unknown error");
|
||||
jq_report_error(jq, jv_string_fmt("jq: error loading data file %s: %s\n", jv_string_value(lib_path), jv_string_value(data)));
|
||||
nerrors++;
|
||||
goto out;
|
||||
} else if (is_data) {
|
||||
// import "foo" as $bar;
|
||||
program = gen_const_global(jv_copy(data), as);
|
||||
} else {
|
||||
// import "foo" as bar;
|
||||
src = locfile_init(jq, jv_string_value(lib_path), jv_string_value(data), jv_string_length_bytes(jv_copy(data)));
|
||||
nerrors += jq_parse_library(src, &program);
|
||||
if (nerrors == 0) {
|
||||
char *lib_origin = strdup(jv_string_value(lib_path));
|
||||
nerrors += process_dependencies(jq, jq_get_jq_origin(jq),
|
||||
jv_string(dirname(lib_origin)),
|
||||
&program, lib_state);
|
||||
free(lib_origin);
|
||||
}
|
||||
}
|
||||
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[state_idx] = strdup(jv_string_value(lib_path));
|
||||
lib_state->defs[state_idx] = program;
|
||||
*out_block = program;
|
||||
if (src)
|
||||
locfile_free(src);
|
||||
out:
|
||||
jv_free(lib_path);
|
||||
jv_free(data);
|
||||
return nerrors;
|
||||
}
|
||||
|
||||
// FIXME It'd be nice to have an option to search the same search path
|
||||
// as we do in process_dependencies.
|
||||
jv load_module_meta(jq_state *jq, jv mod_relpath) {
|
||||
// We can't know the caller's origin; we could though, if it was passed in
|
||||
jv lib_path = find_lib(jq, validate_relpath(mod_relpath), jq_get_lib_dirs(jq), ".jq", jq_get_jq_origin(jq), jv_null());
|
||||
if (!jv_is_valid(lib_path))
|
||||
return lib_path;
|
||||
jv meta = jv_null();
|
||||
jv data = jv_load_file(jv_string_value(lib_path), 1);
|
||||
if (jv_is_valid(data)) {
|
||||
block program;
|
||||
struct locfile* src = locfile_init(jq, jv_string_value(lib_path), jv_string_value(data), jv_string_length_bytes(jv_copy(data)));
|
||||
int nerrors = jq_parse_library(src, &program);
|
||||
if (nerrors == 0) {
|
||||
meta = block_module_meta(program);
|
||||
if (jv_get_kind(meta) == JV_KIND_NULL)
|
||||
meta = jv_object();
|
||||
meta = jv_object_set(meta, jv_string("deps"), block_take_imports(&program));
|
||||
}
|
||||
locfile_free(src);
|
||||
block_free(program);
|
||||
}
|
||||
jv_free(lib_path);
|
||||
jv_free(data);
|
||||
return meta;
|
||||
}
|
||||
|
||||
int load_program(jq_state *jq, struct locfile* src, block *out_block) {
|
||||
int nerrors = 0;
|
||||
block program;
|
||||
struct lib_loading_state lib_state = {0,0,0};
|
||||
nerrors = jq_parse(src, &program);
|
||||
if (nerrors)
|
||||
return nerrors;
|
||||
|
||||
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) {
|
||||
free(lib_state.names[i]);
|
||||
if (nerrors == 0 && !block_is_const(lib_state.defs[i]))
|
||||
libs = block_join(libs, lib_state.defs[i]);
|
||||
else
|
||||
block_free(lib_state.defs[i]);
|
||||
}
|
||||
free(lib_state.names);
|
||||
free(lib_state.defs);
|
||||
if (nerrors)
|
||||
block_free(program);
|
||||
else
|
||||
*out_block = block_drop_unreferenced(block_join(libs, program));
|
||||
|
||||
return nerrors;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef LINKER_H
|
||||
#define LINKER_H
|
||||
|
||||
int load_program(jq_state *jq, struct locfile* src, block *out_block);
|
||||
jv load_module_meta(jq_state *jq, jv modname);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,91 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "jq.h"
|
||||
#include "jv_alloc.h"
|
||||
#include "locfile.h"
|
||||
|
||||
|
||||
struct locfile* locfile_init(jq_state *jq, const char *fname, const char* data, int length) {
|
||||
struct locfile* l = jv_mem_alloc(sizeof(struct locfile));
|
||||
l->jq = jq;
|
||||
l->fname = jv_string(fname);
|
||||
l->data = jv_mem_alloc(length);
|
||||
memcpy((char*)l->data,data,length);
|
||||
l->length = length;
|
||||
l->nlines = 1;
|
||||
l->refct = 1;
|
||||
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[0] = 0;
|
||||
int line = 1;
|
||||
for (int i=0; i<length; i++) {
|
||||
if (data[i] == '\n') {
|
||||
l->linemap[line] = i+1; // at start of line, not of \n
|
||||
line++;
|
||||
}
|
||||
}
|
||||
l->linemap[l->nlines] = length+1; // virtual last \n
|
||||
return l;
|
||||
}
|
||||
|
||||
struct locfile* locfile_retain(struct locfile* l) {
|
||||
l->refct++;
|
||||
return l;
|
||||
}
|
||||
void locfile_free(struct locfile* l) {
|
||||
if (--(l->refct) == 0) {
|
||||
jv_free(l->fname);
|
||||
jv_mem_free(l->linemap);
|
||||
jv_mem_free((char*)l->data);
|
||||
jv_mem_free(l);
|
||||
}
|
||||
}
|
||||
|
||||
int locfile_get_line(struct locfile* l, int pos) {
|
||||
assert(pos < l->length);
|
||||
int line = 1;
|
||||
while (l->linemap[line] <= pos) line++; // == if pos at start (before, never ==, because pos never on \n)
|
||||
assert(line-1 < l->nlines);
|
||||
return line-1;
|
||||
}
|
||||
|
||||
static int locfile_line_length(struct locfile* l, int line) {
|
||||
assert(line < l->nlines);
|
||||
return l->linemap[line+1] - l->linemap[line] -1; // -1 to omit \n
|
||||
}
|
||||
|
||||
void locfile_locate(struct locfile* l, location loc, const char* fmt, ...) {
|
||||
va_list fmtargs;
|
||||
va_start(fmtargs, fmt);
|
||||
int startline;
|
||||
int offset;
|
||||
|
||||
if (loc.start != -1) {
|
||||
startline = locfile_get_line(l, loc.start);
|
||||
offset = l->linemap[startline];
|
||||
}
|
||||
|
||||
jv m1 = jv_string_vfmt(fmt, fmtargs);
|
||||
if (!jv_is_valid(m1)) {
|
||||
jq_report_error(l->jq, m1);
|
||||
return;
|
||||
}
|
||||
if (loc.start == -1) {
|
||||
jq_report_error(l->jq, jv_string_fmt("jq: error: %s\n<unknown location>", jv_string_value(m1)));
|
||||
jv_free(m1);
|
||||
return;
|
||||
}
|
||||
jv m2 = jv_string_fmt("%s at %s, line %d:\n%.*s%*s", jv_string_value(m1),
|
||||
jv_string_value(l->fname), startline + 1,
|
||||
locfile_line_length(l, startline), l->data + offset,
|
||||
loc.start - offset, "");
|
||||
jv_free(m1);
|
||||
jq_report_error(l->jq, m2);
|
||||
return;
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
#ifndef _LOCFILE_H
|
||||
#define _LOCFILE_H
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include "jv_alloc.h"
|
||||
#ifndef LOCFILE_H
|
||||
#define LOCFILE_H
|
||||
|
||||
#include "jq.h"
|
||||
|
||||
typedef struct {
|
||||
int start, end;
|
||||
} location;
|
||||
@@ -12,68 +10,20 @@ typedef struct {
|
||||
static const location UNKNOWN_LOCATION = {-1, -1};
|
||||
|
||||
struct locfile {
|
||||
jv fname;
|
||||
const char* data;
|
||||
int length;
|
||||
int* linemap;
|
||||
int nlines;
|
||||
char *error;
|
||||
jq_state *jq;
|
||||
int refct;
|
||||
};
|
||||
|
||||
static void locfile_init(struct locfile* l, const char* data, int length) {
|
||||
l->data = data;
|
||||
l->length = length;
|
||||
l->nlines = 1;
|
||||
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[0] = 0;
|
||||
int line = 1;
|
||||
for (int i=0; i<length; i++) {
|
||||
if (data[i] == '\n') {
|
||||
l->linemap[line] = i;
|
||||
line++;
|
||||
}
|
||||
}
|
||||
l->linemap[l->nlines] = length;
|
||||
}
|
||||
|
||||
static void locfile_free(struct locfile* l) {
|
||||
jv_mem_free(l->linemap);
|
||||
}
|
||||
|
||||
static int locfile_get_line(struct locfile* l, int pos) {
|
||||
assert(pos < l->length);
|
||||
int line = 0;
|
||||
while (l->linemap[line+1] < pos) line++;
|
||||
assert(line < l->nlines);
|
||||
return line;
|
||||
}
|
||||
|
||||
static int locfile_line_length(struct locfile* l, int line) {
|
||||
assert(line < l->nlines);
|
||||
return l->linemap[line+1] - l->linemap[line];
|
||||
}
|
||||
|
||||
static void locfile_locate(struct locfile* l, location loc, const char* fmt, ...) {
|
||||
va_list fmtargs;
|
||||
va_start(fmtargs, fmt);
|
||||
vfprintf(stderr, fmt, fmtargs);
|
||||
va_end(fmtargs);
|
||||
fprintf(stderr, "\n");
|
||||
if (loc.start == -1) {
|
||||
fprintf(stderr, "<unknown location>\n");
|
||||
return;
|
||||
}
|
||||
int startline = locfile_get_line(l, loc.start);
|
||||
int offset = l->linemap[startline];
|
||||
fprintf(stderr, "%.*s\n", locfile_line_length(l, startline), l->data + offset);
|
||||
fprintf(stderr, "%*s", loc.start - offset, "");
|
||||
for (int i = loc.start;
|
||||
i < loc.end && i < offset + locfile_line_length(l, startline);
|
||||
i++){
|
||||
fprintf(stderr, "^");
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
struct locfile* locfile_init(jq_state *, const char *, const char *, int);
|
||||
struct locfile* locfile_retain(struct locfile *);
|
||||
int locfile_get_line(struct locfile *, int);
|
||||
void locfile_free(struct locfile *);
|
||||
void locfile_locate(struct locfile *, location, const char *, ...);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,177 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_compare_version.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_COMPARE_VERSION(VERSION_A, OP, VERSION_B, [ACTION-IF-TRUE], [ACTION-IF-FALSE])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# This macro compares two version strings. Due to the various number of
|
||||
# minor-version numbers that can exist, and the fact that string
|
||||
# comparisons are not compatible with numeric comparisons, this is not
|
||||
# necessarily trivial to do in a autoconf script. This macro makes doing
|
||||
# these comparisons easy.
|
||||
#
|
||||
# The six basic comparisons are available, as well as checking equality
|
||||
# limited to a certain number of minor-version levels.
|
||||
#
|
||||
# The operator OP determines what type of comparison to do, and can be one
|
||||
# of:
|
||||
#
|
||||
# eq - equal (test A == B)
|
||||
# ne - not equal (test A != B)
|
||||
# le - less than or equal (test A <= B)
|
||||
# ge - greater than or equal (test A >= B)
|
||||
# lt - less than (test A < B)
|
||||
# gt - greater than (test A > B)
|
||||
#
|
||||
# Additionally, the eq and ne operator can have a number after it to limit
|
||||
# the test to that number of minor versions.
|
||||
#
|
||||
# eq0 - equal up to the length of the shorter version
|
||||
# ne0 - not equal up to the length of the shorter version
|
||||
# eqN - equal up to N sub-version levels
|
||||
# neN - not equal up to N sub-version levels
|
||||
#
|
||||
# When the condition is true, shell commands ACTION-IF-TRUE are run,
|
||||
# otherwise shell commands ACTION-IF-FALSE are run. The environment
|
||||
# variable 'ax_compare_version' is always set to either 'true' or 'false'
|
||||
# as well.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# AX_COMPARE_VERSION([3.15.7],[lt],[3.15.8])
|
||||
# AX_COMPARE_VERSION([3.15],[lt],[3.15.8])
|
||||
#
|
||||
# would both be true.
|
||||
#
|
||||
# AX_COMPARE_VERSION([3.15.7],[eq],[3.15.8])
|
||||
# AX_COMPARE_VERSION([3.15],[gt],[3.15.8])
|
||||
#
|
||||
# would both be false.
|
||||
#
|
||||
# AX_COMPARE_VERSION([3.15.7],[eq2],[3.15.8])
|
||||
#
|
||||
# would be true because it is only comparing two minor versions.
|
||||
#
|
||||
# AX_COMPARE_VERSION([3.15.7],[eq0],[3.15])
|
||||
#
|
||||
# would be true because it is only comparing the lesser number of minor
|
||||
# versions of the two values.
|
||||
#
|
||||
# Note: The characters that separate the version numbers do not matter. An
|
||||
# empty string is the same as version 0. OP is evaluated by autoconf, not
|
||||
# configure, so must be a string, not a variable.
|
||||
#
|
||||
# The author would like to acknowledge Guido Draheim whose advice about
|
||||
# the m4_case and m4_ifvaln functions make this macro only include the
|
||||
# portions necessary to perform the specific comparison specified by the
|
||||
# OP argument in the final configure script.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Tim Toolan <toolan@ele.uri.edu>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 11
|
||||
|
||||
dnl #########################################################################
|
||||
AC_DEFUN([AX_COMPARE_VERSION], [
|
||||
AC_REQUIRE([AC_PROG_AWK])
|
||||
|
||||
# Used to indicate true or false condition
|
||||
ax_compare_version=false
|
||||
|
||||
# Convert the two version strings to be compared into a format that
|
||||
# allows a simple string comparison. The end result is that a version
|
||||
# string of the form 1.12.5-r617 will be converted to the form
|
||||
# 0001001200050617. In other words, each number is zero padded to four
|
||||
# digits, and non digits are removed.
|
||||
AS_VAR_PUSHDEF([A],[ax_compare_version_A])
|
||||
A=`echo "$1" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \
|
||||
-e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \
|
||||
-e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \
|
||||
-e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \
|
||||
-e 's/[[^0-9]]//g'`
|
||||
|
||||
AS_VAR_PUSHDEF([B],[ax_compare_version_B])
|
||||
B=`echo "$3" | sed -e 's/\([[0-9]]*\)/Z\1Z/g' \
|
||||
-e 's/Z\([[0-9]]\)Z/Z0\1Z/g' \
|
||||
-e 's/Z\([[0-9]][[0-9]]\)Z/Z0\1Z/g' \
|
||||
-e 's/Z\([[0-9]][[0-9]][[0-9]]\)Z/Z0\1Z/g' \
|
||||
-e 's/[[^0-9]]//g'`
|
||||
|
||||
dnl # In the case of le, ge, lt, and gt, the strings are sorted as necessary
|
||||
dnl # then the first line is used to determine if the condition is true.
|
||||
dnl # The sed right after the echo is to remove any indented white space.
|
||||
m4_case(m4_tolower($2),
|
||||
[lt],[
|
||||
ax_compare_version=`echo "x$A
|
||||
x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/false/;s/x${B}/true/;1q"`
|
||||
],
|
||||
[gt],[
|
||||
ax_compare_version=`echo "x$A
|
||||
x$B" | sed 's/^ *//' | sort | sed "s/x${A}/false/;s/x${B}/true/;1q"`
|
||||
],
|
||||
[le],[
|
||||
ax_compare_version=`echo "x$A
|
||||
x$B" | sed 's/^ *//' | sort | sed "s/x${A}/true/;s/x${B}/false/;1q"`
|
||||
],
|
||||
[ge],[
|
||||
ax_compare_version=`echo "x$A
|
||||
x$B" | sed 's/^ *//' | sort -r | sed "s/x${A}/true/;s/x${B}/false/;1q"`
|
||||
],[
|
||||
dnl Split the operator from the subversion count if present.
|
||||
m4_bmatch(m4_substr($2,2),
|
||||
[0],[
|
||||
# A count of zero means use the length of the shorter version.
|
||||
# Determine the number of characters in A and B.
|
||||
ax_compare_version_len_A=`echo "$A" | $AWK '{print(length)}'`
|
||||
ax_compare_version_len_B=`echo "$B" | $AWK '{print(length)}'`
|
||||
|
||||
# Set A to no more than B's length and B to no more than A's length.
|
||||
A=`echo "$A" | sed "s/\(.\{$ax_compare_version_len_B\}\).*/\1/"`
|
||||
B=`echo "$B" | sed "s/\(.\{$ax_compare_version_len_A\}\).*/\1/"`
|
||||
],
|
||||
[[0-9]+],[
|
||||
# A count greater than zero means use only that many subversions
|
||||
A=`echo "$A" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"`
|
||||
B=`echo "$B" | sed "s/\(\([[0-9]]\{4\}\)\{m4_substr($2,2)\}\).*/\1/"`
|
||||
],
|
||||
[.+],[
|
||||
AC_WARNING(
|
||||
[illegal OP numeric parameter: $2])
|
||||
],[])
|
||||
|
||||
# Pad zeros at end of numbers to make same length.
|
||||
ax_compare_version_tmp_A="$A`echo $B | sed 's/./0/g'`"
|
||||
B="$B`echo $A | sed 's/./0/g'`"
|
||||
A="$ax_compare_version_tmp_A"
|
||||
|
||||
# Check for equality or inequality as necessary.
|
||||
m4_case(m4_tolower(m4_substr($2,0,2)),
|
||||
[eq],[
|
||||
test "x$A" = "x$B" && ax_compare_version=true
|
||||
],
|
||||
[ne],[
|
||||
test "x$A" != "x$B" && ax_compare_version=true
|
||||
],[
|
||||
AC_WARNING([illegal OP parameter: $2])
|
||||
])
|
||||
])
|
||||
|
||||
AS_VAR_POPDEF([A])dnl
|
||||
AS_VAR_POPDEF([B])dnl
|
||||
|
||||
dnl # Execute ACTION-IF-TRUE / ACTION-IF-FALSE.
|
||||
if test "$ax_compare_version" = "true" ; then
|
||||
m4_ifvaln([$4],[$4],[:])dnl
|
||||
m4_ifvaln([$5],[else $5])dnl
|
||||
fi
|
||||
]) dnl AX_COMPARE_VERSION
|
||||
@@ -0,0 +1,68 @@
|
||||
# ===========================================================================
|
||||
# Modified from
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_prog_perl_version.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_PROG_BISON_VERSION([VERSION],[ACTION-IF-TRUE],[ACTION-IF-FALSE])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Makes sure that bison supports the version indicated. If true the shell
|
||||
# commands in ACTION-IF-TRUE are executed. If not the shell commands in
|
||||
# ACTION-IF-FALSE are run. Note if $PERL is not set the macro will fail.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# AC_PROG_YACC
|
||||
# AX_PROG_BISON_VERSION([3.0.0],[ ... ],[ ... ])
|
||||
#
|
||||
# This will check to make sure that the bison you have supports at least
|
||||
# version 3.0.0.
|
||||
#
|
||||
# NOTE: This macro uses the $YACC variable to perform the check.
|
||||
# AX_WITH_YACC can be used to set that variable prior to running this
|
||||
# macro. The $BISON_VERSION variable will be set with the detected
|
||||
# version.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2009 Francesco Salvestrini <salvestrini@users.sourceforge.net>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 12
|
||||
|
||||
AC_DEFUN([AX_PROG_BISON_VERSION],[
|
||||
AC_REQUIRE([AC_PROG_SED])
|
||||
AC_REQUIRE([AC_PROG_GREP])
|
||||
|
||||
AS_IF([test -n "$YACC"],[
|
||||
ax_bison_version="$1"
|
||||
|
||||
AC_MSG_CHECKING([for bison version])
|
||||
changequote(<<,>>)
|
||||
bison_version=`$YACC --version 2>&1 \
|
||||
| $GREP bison \
|
||||
| $SED -e 's/^[^0-9]*//' -e 's/[^0-9]*$//'`
|
||||
changequote([,])
|
||||
AC_MSG_RESULT($bison_version)
|
||||
|
||||
AC_SUBST([BISON_VERSION],[$bison_version])
|
||||
|
||||
AX_COMPARE_VERSION([$ax_bison_version],[le],[$bison_version],[
|
||||
:
|
||||
$2
|
||||
],[
|
||||
:
|
||||
$3
|
||||
])
|
||||
],[
|
||||
AC_MSG_WARN([could not find bison])
|
||||
$3
|
||||
])
|
||||
])
|
||||
@@ -1,33 +1,76 @@
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <libgen.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
#include <processenv.h>
|
||||
#include <shellapi.h>
|
||||
#include <wchar.h>
|
||||
#include <wtypes.h>
|
||||
#endif
|
||||
|
||||
#include "compile.h"
|
||||
#include "jv.h"
|
||||
#include "jv_parse.h"
|
||||
#include "execute.h"
|
||||
#include "config.h" /* Autoconf generated header file */
|
||||
#include "jq.h"
|
||||
#include "jv_alloc.h"
|
||||
#include "util.h"
|
||||
#include "version.h"
|
||||
|
||||
int jq_testsuite(int argc, char* argv[]);
|
||||
int jq_testsuite(jv lib_dirs, int verbose, int argc, char* argv[]);
|
||||
|
||||
static const char* progname;
|
||||
|
||||
static void usage() {
|
||||
fprintf(stderr, "\njq - commandline JSON processor [version %s]\n", PACKAGE_VERSION);
|
||||
fprintf(stderr, "Usage: %s [options] <jq filter> [file...]\n\n", progname);
|
||||
fprintf(stderr, "For a description of the command line options and\n");
|
||||
fprintf(stderr, "how to write jq filters (and why you might want to)\n");
|
||||
fprintf(stderr, "see the jq manpage, or the online documentation at\n");
|
||||
fprintf(stderr, "http://stedolan.github.io/jq\n\n");
|
||||
exit(1);
|
||||
/*
|
||||
* For a longer help message we could use a better option parsing
|
||||
* strategy, one that lets stack options.
|
||||
*/
|
||||
static void usage(int code) {
|
||||
FILE *f = stderr;
|
||||
|
||||
if (code == 0)
|
||||
f = stdout;
|
||||
|
||||
int ret = fprintf(f,
|
||||
"jq - commandline JSON processor [version %s]\n"
|
||||
"Usage: %s [options] <jq filter> [file...]\n\n"
|
||||
"\tjq is a tool for processing JSON inputs, applying the\n"
|
||||
"\tgiven filter to its JSON text inputs and producing the\n"
|
||||
"\tfilter's results as JSON on standard output.\n"
|
||||
"\tThe simplest filter is ., which is the identity filter,\n"
|
||||
"\tcopying jq's input to its output unmodified (except for\n"
|
||||
"\tformatting).\n"
|
||||
"\tFor more advanced filters see the jq(1) manpage (\"man jq\")\n"
|
||||
"\tand/or http://stedolan.github.io/jq\n\n"
|
||||
"\tSome of the options include:\n"
|
||||
"\t -c\t\tcompact instead of pretty-printed output;\n"
|
||||
"\t -n\t\tuse `null` as the single input value;\n"
|
||||
"\t -e\t\tset the exit status code based on the output;\n"
|
||||
"\t -s\t\tread (slurp) all inputs into an array; apply filter to it;\n"
|
||||
"\t -r\t\toutput raw strings, not JSON texts;\n"
|
||||
"\t -R\t\tread raw strings, not JSON texts;\n"
|
||||
"\t -C\t\tcolorize JSON;\n"
|
||||
"\t -M\t\tmonochrome (don't colorize JSON);\n"
|
||||
"\t -S\t\tsort keys of objects on output;\n"
|
||||
"\t --tab\tuse tabs for indentation;\n"
|
||||
"\t --arg a v\tset variable $a to value <v>;\n"
|
||||
"\t --argjson a v\tset variable $a to JSON value <v>;\n"
|
||||
"\t --slurpfile a f\tset variable $a to an array of JSON texts read from <f>;\n"
|
||||
"\tSee the manpage for more options.\n", JQ_VERSION, progname);
|
||||
exit((ret < 0 && code == 0) ? 2 : code);
|
||||
}
|
||||
|
||||
static void die() {
|
||||
fprintf(stderr, "Use %s --help for help with command-line options,\n", progname);
|
||||
fprintf(stderr, "or see the jq documentation at http://stedolan.github.io/jq\n");
|
||||
exit(1);
|
||||
fprintf(stderr, "or see the jq manpage, or online docs at http://stedolan.github.io/jq\n");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,263 +80,487 @@ static int isoptish(const char* text) {
|
||||
return text[0] == '-' && (text[1] == '-' || isalpha(text[1]));
|
||||
}
|
||||
|
||||
static int isoption(const char* text, char shortopt, const char* longopt) {
|
||||
static int isoption(const char* text, char shortopt, const char* longopt, size_t *short_opts) {
|
||||
if (text[0] != '-' || text[1] == '-')
|
||||
*short_opts = 0;
|
||||
if (text[0] != '-') return 0;
|
||||
if (strlen(text) == 2 && text[1] == shortopt) return 1;
|
||||
|
||||
// check long option
|
||||
if (text[1] == '-' && !strcmp(text+2, longopt)) return 1;
|
||||
else if (text[1] == '-') return 0;
|
||||
|
||||
// must be short option; check it and...
|
||||
if (!shortopt) return 0;
|
||||
if (strchr(text, shortopt) != NULL) {
|
||||
(*short_opts)++; // ...count it (for option stacking)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum {
|
||||
SLURP = 1,
|
||||
RAW_INPUT = 2,
|
||||
PROVIDE_NULL = 4,
|
||||
|
||||
RAW_OUTPUT = 8,
|
||||
COMPACT_OUTPUT = 16,
|
||||
ASCII_OUTPUT = 32,
|
||||
COLOUR_OUTPUT = 64,
|
||||
NO_COLOUR_OUTPUT = 128,
|
||||
|
||||
FROM_FILE = 256,
|
||||
|
||||
SLURP = 1,
|
||||
RAW_INPUT = 2,
|
||||
PROVIDE_NULL = 4,
|
||||
RAW_OUTPUT = 8,
|
||||
ASCII_OUTPUT = 32,
|
||||
COLOUR_OUTPUT = 64,
|
||||
NO_COLOUR_OUTPUT = 128,
|
||||
SORTED_OUTPUT = 256,
|
||||
FROM_FILE = 512,
|
||||
RAW_NO_LF = 1024,
|
||||
UNBUFFERED_OUTPUT = 2048,
|
||||
EXIT_STATUS = 4096,
|
||||
SEQ = 8192,
|
||||
RUN_TESTS = 16384,
|
||||
/* debugging only */
|
||||
DUMP_DISASM = 2048,
|
||||
DUMP_DISASM = 32768,
|
||||
};
|
||||
static int options = 0;
|
||||
static struct bytecode* bc;
|
||||
|
||||
static void process(jv value, int flags) {
|
||||
jq_state *jq = NULL;
|
||||
jq_init(bc, value, &jq, flags);
|
||||
static int process(jq_state *jq, jv value, int flags, int dumpopts) {
|
||||
int ret = 14; // 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;
|
||||
jv_free(result);
|
||||
} else {
|
||||
int dumpopts;
|
||||
/* Disable colour by default on Windows builds as Windows
|
||||
terminals tend not to display it correctly */
|
||||
#ifdef WIN32
|
||||
dumpopts = 0;
|
||||
#else
|
||||
dumpopts = isatty(fileno(stdout)) ? JV_PRINT_COLOUR : 0;
|
||||
#endif
|
||||
if (!(options & COMPACT_OUTPUT)) dumpopts |= JV_PRINT_PRETTY;
|
||||
if (options & ASCII_OUTPUT) dumpopts |= JV_PRINT_ASCII;
|
||||
if (options & COLOUR_OUTPUT) dumpopts |= JV_PRINT_COLOUR;
|
||||
if (options & NO_COLOUR_OUTPUT) dumpopts &= ~JV_PRINT_COLOUR;
|
||||
if (jv_get_kind(result) == JV_KIND_FALSE || jv_get_kind(result) == JV_KIND_NULL)
|
||||
ret = 11;
|
||||
else
|
||||
ret = 0;
|
||||
if (options & SEQ)
|
||||
priv_fwrite("\036", 1, stdout, dumpopts & JV_PRINT_ISATTY);
|
||||
jv_dump(result, dumpopts);
|
||||
}
|
||||
printf("\n");
|
||||
if (!(options & RAW_NO_LF))
|
||||
priv_fwrite("\n", 1, stdout, dumpopts & JV_PRINT_ISATTY);
|
||||
if (options & UNBUFFERED_OUTPUT)
|
||||
fflush(stdout);
|
||||
}
|
||||
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);
|
||||
if (jv_get_kind(msg) == JV_KIND_STRING) {
|
||||
fprintf(stderr, "jq: error (at %s): %s\n",
|
||||
jv_string_value(input_pos), jv_string_value(msg));
|
||||
} else {
|
||||
msg = jv_dump_string(msg, 0);
|
||||
fprintf(stderr, "jq: error (at %s) (not a string): %s\n",
|
||||
jv_string_value(input_pos), jv_string_value(msg));
|
||||
}
|
||||
ret = 5;
|
||||
jv_free(input_pos);
|
||||
jv_free(msg);
|
||||
}
|
||||
jv_free(result);
|
||||
jq_teardown(&jq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static jv slurp_file(const char* filename) {
|
||||
FILE* file = fopen(filename, "r");
|
||||
if (!file) {
|
||||
return jv_invalid_with_msg(jv_string_fmt("Could not open %s: %s",
|
||||
filename,
|
||||
strerror(errno)));
|
||||
}
|
||||
jv data = jv_string("");
|
||||
while (!feof(file) && !ferror(file)) {
|
||||
char buf[4096];
|
||||
size_t n = fread(buf, 1, sizeof(buf), file);
|
||||
data = jv_string_concat(data, jv_string_sized(buf, (int)n));
|
||||
}
|
||||
int badread = ferror(file);
|
||||
fclose(file);
|
||||
if (badread) {
|
||||
jv_free(data);
|
||||
return jv_invalid_with_msg(jv_string_fmt("Error reading from %s",
|
||||
filename));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
FILE* current_input;
|
||||
const char** input_filenames;
|
||||
int ninput_files;
|
||||
int next_input_idx;
|
||||
static int read_more(char* buf, size_t size) {
|
||||
while (!current_input || feof(current_input)) {
|
||||
if (current_input) {
|
||||
fclose(current_input);
|
||||
current_input = 0;
|
||||
}
|
||||
if (next_input_idx == ninput_files) {
|
||||
return 0;
|
||||
}
|
||||
if (!strcmp(input_filenames[next_input_idx], "-")) {
|
||||
current_input = stdin;
|
||||
} else {
|
||||
current_input = fopen(input_filenames[next_input_idx], "r");
|
||||
}
|
||||
if (!current_input) {
|
||||
fprintf(stderr, "%s: %s: %s\n", progname, input_filenames[next_input_idx], strerror(errno));
|
||||
}
|
||||
next_input_idx++;
|
||||
}
|
||||
|
||||
if (!fgets(buf, size, current_input)) buf[0] = 0;
|
||||
return 1;
|
||||
static void debug_cb(void *data, jv input) {
|
||||
int dumpopts = *(int *)data;
|
||||
jv_dumpf(JV_ARRAY(jv_string("DEBUG:"), input), stderr, dumpopts & ~(JV_PRINT_PRETTY));
|
||||
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);
|
||||
size_t arg_sz;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
argv[i] = alloca((arg_sz = WideCharToMultiByte(CP_UTF8,
|
||||
0,
|
||||
wargv[i],
|
||||
-1, 0, 0, 0, 0)));
|
||||
WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, argv[i], arg_sz, 0, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (argc) progname = argv[0];
|
||||
|
||||
if (argc > 1 && !strcmp(argv[1], "--run-tests")) {
|
||||
return jq_testsuite(argc - 1, argv + 1);
|
||||
jq = jq_init();
|
||||
if (jq == NULL) {
|
||||
perror("malloc");
|
||||
ret = 2;
|
||||
goto out;
|
||||
}
|
||||
|
||||
int dumpopts = JV_PRINT_INDENT_FLAGS(2);
|
||||
const char* program = 0;
|
||||
input_filenames = jv_mem_alloc(sizeof(const char*) * argc);
|
||||
ninput_files = 0;
|
||||
|
||||
jq_util_input_state *input_state = jq_util_input_init(NULL, NULL); // XXX add err_cb
|
||||
|
||||
int further_args_are_files = 0;
|
||||
int jq_flags = 0;
|
||||
jv program_arguments = jv_array();
|
||||
for (int i=1; i<argc; i++) {
|
||||
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) {
|
||||
input_filenames[ninput_files++] = argv[i];
|
||||
jq_util_input_add_input(input_state, argv[i]);
|
||||
nfiles++;
|
||||
} else if (!strcmp(argv[i], "--")) {
|
||||
if (!program) usage();
|
||||
if (!program) usage(2);
|
||||
further_args_are_files = 1;
|
||||
} else if (!isoptish(argv[i])) {
|
||||
if (program) {
|
||||
input_filenames[ninput_files++] = argv[i];
|
||||
jq_util_input_add_input(input_state, argv[i]);
|
||||
nfiles++;
|
||||
} else {
|
||||
program = argv[i];
|
||||
}
|
||||
} else if (isoption(argv[i], 's', "slurp")) {
|
||||
options |= SLURP;
|
||||
} else if (isoption(argv[i], 'r', "raw-output")) {
|
||||
options |= RAW_OUTPUT;
|
||||
} else if (isoption(argv[i], 'c', "compact-output")) {
|
||||
options |= COMPACT_OUTPUT;
|
||||
} else if (isoption(argv[i], 'C', "color-output")) {
|
||||
options |= COLOUR_OUTPUT;
|
||||
} else if (isoption(argv[i], 'M', "monochrome-output")) {
|
||||
options |= NO_COLOUR_OUTPUT;
|
||||
} else if (isoption(argv[i], 'a', "ascii-output")) {
|
||||
options |= ASCII_OUTPUT;
|
||||
} else if (isoption(argv[i], 'R', "raw-input")) {
|
||||
options |= RAW_INPUT;
|
||||
} else if (isoption(argv[i], 'n', "null-input")) {
|
||||
options |= PROVIDE_NULL;
|
||||
} else if (isoption(argv[i], 'f', "from-file")) {
|
||||
options |= FROM_FILE;
|
||||
} else if (isoption(argv[i], 0, "arg")) {
|
||||
if (i >= argc - 2) {
|
||||
fprintf(stderr, "%s: --arg takes two parameters (e.g. -a varname value)\n", progname);
|
||||
} else {
|
||||
if (argv[i][1] == 'L') {
|
||||
if (jv_get_kind(lib_search_paths) == JV_KIND_NULL)
|
||||
lib_search_paths = jv_array();
|
||||
if (argv[i][2] != 0) { // -Lname (faster check than strlen)
|
||||
lib_search_paths = jv_array_append(lib_search_paths, jv_string(argv[i]+2));
|
||||
} else if (i >= argc - 1) {
|
||||
fprintf(stderr, "-L takes a parameter: (e.g. -L /search/path or -L/search/path)\n");
|
||||
die();
|
||||
} else {
|
||||
lib_search_paths = jv_array_append(lib_search_paths, jv_string(argv[i+1]));
|
||||
i++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isoption(argv[i], 's', "slurp", &short_opts)) {
|
||||
options |= SLURP;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 'r', "raw-output", &short_opts)) {
|
||||
options |= RAW_OUTPUT;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 'c', "compact-output", &short_opts)) {
|
||||
dumpopts &= ~(JV_PRINT_TAB | JV_PRINT_INDENT_FLAGS(7));
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 'C', "color-output", &short_opts)) {
|
||||
options |= COLOUR_OUTPUT;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 'M', "monochrome-output", &short_opts)) {
|
||||
options |= NO_COLOUR_OUTPUT;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 'a', "ascii-output", &short_opts)) {
|
||||
options |= ASCII_OUTPUT;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 0, "unbuffered", &short_opts)) {
|
||||
options |= UNBUFFERED_OUTPUT;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 'S', "sort-keys", &short_opts)) {
|
||||
options |= SORTED_OUTPUT;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 'R', "raw-input", &short_opts)) {
|
||||
options |= RAW_INPUT;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 'n', "null-input", &short_opts)) {
|
||||
options |= PROVIDE_NULL;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 'f', "from-file", &short_opts)) {
|
||||
options |= FROM_FILE;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 'j', "join-output", &short_opts)) {
|
||||
options |= RAW_OUTPUT | RAW_NO_LF;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (isoption(argv[i], 0, "indent", &short_opts)) {
|
||||
if (i >= argc - 1) {
|
||||
fprintf(stderr, "%s: --indent takes one parameter\n", progname);
|
||||
die();
|
||||
}
|
||||
dumpopts &= ~(JV_PRINT_TAB | JV_PRINT_INDENT_FLAGS(7));
|
||||
int indent = atoi(argv[i+1]);
|
||||
if (indent < -1 || indent > 7) {
|
||||
fprintf(stderr, "%s: --indent takes a number between -1 and 7\n", progname);
|
||||
die();
|
||||
}
|
||||
dumpopts |= JV_PRINT_INDENT_FLAGS(indent);
|
||||
i++;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 0, "seq", &short_opts)) {
|
||||
options |= SEQ;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 0, "stream", &short_opts)) {
|
||||
parser_flags |= JV_PARSE_STREAMING;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 0, "stream-errors", &short_opts)) {
|
||||
parser_flags |= JV_PARSE_STREAM_ERRORS;
|
||||
if (!short_opts) 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, "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);
|
||||
i += 2; // skip the next two arguments
|
||||
if (!short_opts) 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();
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (isoption(argv[i], 0, "argfile", &short_opts) ||
|
||||
isoption(argv[i], 0, "slurpfile", &short_opts)) {
|
||||
const char *which;
|
||||
if (isoption(argv[i], 0, "argfile", &short_opts))
|
||||
which = "argfile";
|
||||
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 (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;
|
||||
}
|
||||
if (isoption(argv[i], 0, "debug-dump-disasm", &short_opts)) {
|
||||
options |= DUMP_DISASM;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 0, "debug-trace", &short_opts)) {
|
||||
jq_flags |= JQ_DEBUG_TRACE;
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 'h', "help", &short_opts)) {
|
||||
usage(0);
|
||||
if (!short_opts) continue;
|
||||
}
|
||||
if (isoption(argv[i], 'V', "version", &short_opts)) {
|
||||
printf("jq-%s\n", JQ_VERSION);
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
if (isoption(argv[i], 0, "run-tests", &short_opts)) {
|
||||
i++;
|
||||
// XXX Pass program_arguments, even a whole jq_state *, through;
|
||||
// could be useful for testing
|
||||
ret = jq_testsuite(lib_search_paths,
|
||||
(options & DUMP_DISASM) || (jq_flags & JQ_DEBUG_TRACE),
|
||||
argc - i, argv + i);
|
||||
goto out;
|
||||
}
|
||||
|
||||
// check for unknown options... if this argument was a short option
|
||||
if (strlen(argv[i]) != short_opts + 1) {
|
||||
fprintf(stderr, "%s: Unknown option %s\n", progname, argv[i]);
|
||||
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);
|
||||
i += 2; // skip the next two arguments
|
||||
} else if (isoption(argv[i], 0, "debug-dump-disasm")) {
|
||||
options |= DUMP_DISASM;
|
||||
} else if (isoption(argv[i], 0, "debug-trace")) {
|
||||
jq_flags |= JQ_DEBUG_TRACE;
|
||||
} else if (isoption(argv[i], 'h', "help")) {
|
||||
usage();
|
||||
} else if (isoption(argv[i], 'V', "version")) {
|
||||
fprintf(stderr, "jq version %s\n", PACKAGE_VERSION);
|
||||
return 0;
|
||||
} else {
|
||||
fprintf(stderr, "%s: Unknown option %s\n", progname, argv[i]);
|
||||
die();
|
||||
}
|
||||
}
|
||||
if (!program) usage();
|
||||
if (ninput_files == 0) current_input = stdin;
|
||||
|
||||
if ((options & PROVIDE_NULL) && (options & (RAW_INPUT | SLURP))) {
|
||||
fprintf(stderr, "%s: --null-input cannot be used with --raw-input or --slurp\n", progname);
|
||||
die();
|
||||
if (isatty(fileno(stdout))) {
|
||||
dumpopts |= JV_PRINT_ISATTY;
|
||||
#ifndef WIN32
|
||||
/* Disable colour by default on Windows builds as Windows
|
||||
terminals tend not to display it correctly */
|
||||
dumpopts |= JV_PRINT_COLOUR;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (options & SORTED_OUTPUT) dumpopts |= JV_PRINT_SORTED;
|
||||
if (options & ASCII_OUTPUT) dumpopts |= JV_PRINT_ASCII;
|
||||
if (options & COLOUR_OUTPUT) dumpopts |= JV_PRINT_COLOUR;
|
||||
if (options & NO_COLOUR_OUTPUT) dumpopts &= ~JV_PRINT_COLOUR;
|
||||
|
||||
if (jv_get_kind(lib_search_paths) == JV_KIND_NULL) {
|
||||
// Default search path list
|
||||
lib_search_paths = JV_ARRAY(jv_string("~/.jq"),
|
||||
jv_string("$ORIGIN/../lib/jq"),
|
||||
jv_string("$ORIGIN/lib"));
|
||||
}
|
||||
jq_set_attr(jq, jv_string("JQ_LIBRARY_PATH"), lib_search_paths);
|
||||
|
||||
char *origin = strdup(argv[0]);
|
||||
if (origin == NULL) {
|
||||
fprintf(stderr, "Error: out of memory\n");
|
||||
exit(1);
|
||||
}
|
||||
jq_set_attr(jq, jv_string("JQ_ORIGIN"), jv_string(dirname(origin)));
|
||||
free(origin);
|
||||
|
||||
if (strchr(JQ_VERSION, '-') == NULL)
|
||||
jq_set_attr(jq, jv_string("VERSION_DIR"), jv_string(JQ_VERSION));
|
||||
else
|
||||
jq_set_attr(jq, jv_string("VERSION_DIR"), jv_string_fmt("%.*s-master", (int)(strchr(JQ_VERSION, '-') - JQ_VERSION), JQ_VERSION));
|
||||
|
||||
#if (!defined(WIN32) && defined(HAVE_ISATTY)) || defined(HAVE__ISATTY)
|
||||
|
||||
#if defined(HAVE__ISATTY) && defined(isatty)
|
||||
#undef isatty
|
||||
#define isatty _isatty
|
||||
#endif
|
||||
|
||||
if (!program && isatty(STDOUT_FILENO) && !isatty(STDIN_FILENO))
|
||||
program = ".";
|
||||
#endif
|
||||
|
||||
if (!program) usage(2);
|
||||
|
||||
if (options & FROM_FILE) {
|
||||
jv data = slurp_file(program);
|
||||
char *program_origin = strdup(program);
|
||||
if (program_origin == NULL) {
|
||||
perror("malloc");
|
||||
exit(2);
|
||||
}
|
||||
|
||||
jv data = jv_load_file(program, 1);
|
||||
if (!jv_is_valid(data)) {
|
||||
data = jv_invalid_get_msg(data);
|
||||
fprintf(stderr, "%s: %s\n", progname, jv_string_value(data));
|
||||
jv_free(data);
|
||||
return 1;
|
||||
ret = 2;
|
||||
goto out;
|
||||
}
|
||||
bc = jq_compile_args(jv_string_value(data), program_arguments);
|
||||
jq_set_attr(jq, jv_string("PROGRAM_ORIGIN"), jq_realpath(jv_string(dirname(program_origin))));
|
||||
compiled = jq_compile_args(jq, jv_string_value(data), jv_copy(program_arguments));
|
||||
free(program_origin);
|
||||
jv_free(data);
|
||||
} else {
|
||||
bc = jq_compile_args(program, program_arguments);
|
||||
jq_set_attr(jq, jv_string("PROGRAM_ORIGIN"), jq_realpath(jv_string("."))); // XXX is this good?
|
||||
compiled = jq_compile_args(jq, program, jv_copy(program_arguments));
|
||||
}
|
||||
if (!compiled){
|
||||
ret = 3;
|
||||
goto out;
|
||||
}
|
||||
if (!bc) return 1;
|
||||
|
||||
if (options & DUMP_DISASM) {
|
||||
dump_disassembly(0, bc);
|
||||
jq_dump_disassembly(jq, 0);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if ((options & SEQ))
|
||||
parser_flags |= JV_PARSE_SEQ;
|
||||
|
||||
if ((options & RAW_INPUT))
|
||||
jq_util_input_set_parser(input_state, NULL, (options & SLURP) ? 1 : 0);
|
||||
else
|
||||
jq_util_input_set_parser(input_state, jv_parser_new(parser_flags), (options & SLURP) ? 1 : 0);
|
||||
|
||||
// Let jq program read from inputs
|
||||
jq_set_input_cb(jq, jq_util_input_next_input_cb, input_state);
|
||||
|
||||
// Let jq program call `debug` builtin and have that go somewhere
|
||||
jq_set_debug_cb(jq, debug_cb, &dumpopts);
|
||||
|
||||
if (nfiles == 0)
|
||||
jq_util_input_add_input(input_state, "-");
|
||||
|
||||
if (options & PROVIDE_NULL) {
|
||||
process(jv_null(), jq_flags);
|
||||
ret = process(jq, jv_null(), jq_flags, dumpopts);
|
||||
} else {
|
||||
jv slurped;
|
||||
if (options & SLURP) {
|
||||
if (options & RAW_INPUT) {
|
||||
slurped = jv_string("");
|
||||
} else {
|
||||
slurped = jv_array();
|
||||
jv value;
|
||||
while (jq_util_input_errors(input_state) == 0 &&
|
||||
(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);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
struct jv_parser parser;
|
||||
jv_parser_init(&parser);
|
||||
char buf[4096];
|
||||
while (read_more(buf, sizeof(buf))) {
|
||||
if (options & RAW_INPUT) {
|
||||
int len = strlen(buf);
|
||||
if (len > 0) {
|
||||
if (options & SLURP) {
|
||||
slurped = jv_string_concat(slurped, jv_string(buf));
|
||||
} else {
|
||||
if (buf[len-1] == '\n') buf[len-1] = 0;
|
||||
process(jv_string(buf), jq_flags);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
jv_parser_set_buf(&parser, buf, strlen(buf), !feof(stdin));
|
||||
jv value;
|
||||
while (jv_is_valid((value = jv_parser_next(&parser)))) {
|
||||
if (options & SLURP) {
|
||||
slurped = jv_array_append(slurped, value);
|
||||
} else {
|
||||
process(value, jq_flags);
|
||||
}
|
||||
}
|
||||
if (jv_invalid_has_msg(jv_copy(value))) {
|
||||
jv msg = jv_invalid_get_msg(value);
|
||||
fprintf(stderr, "parse error: %s\n", jv_string_value(msg));
|
||||
jv_free(msg);
|
||||
ret = 1;
|
||||
break;
|
||||
} else {
|
||||
jv_free(value);
|
||||
}
|
||||
|
||||
// Parse error
|
||||
jv msg = jv_invalid_get_msg(value);
|
||||
if (!(options & SEQ)) {
|
||||
// --seq -> errors are not fatal
|
||||
ret = 4;
|
||||
fprintf(stderr, "parse error: %s\n", jv_string_value(msg));
|
||||
jv_free(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
jv_parser_free(&parser);
|
||||
if (ret != 0)
|
||||
goto out;
|
||||
if (options & SLURP) {
|
||||
process(slurped, jq_flags);
|
||||
fprintf(stderr, "ignoring parse error: %s\n", jv_string_value(msg));
|
||||
jv_free(msg);
|
||||
}
|
||||
}
|
||||
|
||||
if (jq_util_input_errors(input_state) != 0)
|
||||
ret = 2;
|
||||
|
||||
out:
|
||||
jv_mem_free(input_filenames);
|
||||
bytecode_free(bc);
|
||||
badwrite = ferror(stdout);
|
||||
if (fclose(stdout)!=0 || badwrite) {
|
||||
fprintf(stderr,"Error: writing output failed: %s\n", strerror(errno));
|
||||
ret = 2;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
#include "opcode.h"
|
||||
|
||||
// flags, length
|
||||
#define NONE 0, 1
|
||||
#define CONSTANT OP_HAS_CONSTANT, 2
|
||||
#define VARIABLE (OP_HAS_VARIABLE | OP_HAS_BINDING), 3
|
||||
#define BRANCH OP_HAS_BRANCH, 2
|
||||
#define CFUNC (OP_HAS_CFUNC | OP_HAS_BINDING), 3
|
||||
#define UFUNC (OP_HAS_UFUNC | OP_HAS_BINDING | OP_IS_CALL_PSEUDO), 4
|
||||
#define DEFINITION (OP_IS_CALL_PSEUDO | OP_HAS_BINDING), 0
|
||||
#define CLOSURE_REF_IMM (OP_IS_CALL_PSEUDO | OP_HAS_BINDING), 2
|
||||
|
||||
#define OP(name, imm, in, out) \
|
||||
{name, #name, imm, in, out},
|
||||
|
||||
static const struct opcode_description opcode_descriptions[] = {
|
||||
#include "opcode_list.h"
|
||||
};
|
||||
|
||||
static const struct opcode_description invalid_opcode_description = {
|
||||
-1, "#INVALID", 0, 0, 0, 0
|
||||
};
|
||||
|
||||
|
||||
const struct opcode_description* opcode_describe(opcode op) {
|
||||
if ((int)op >= 0 && (int)op < NUM_OPCODES) {
|
||||
return &opcode_descriptions[op];
|
||||
} else {
|
||||
return &invalid_opcode_description;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
#ifndef OPCODE_H
|
||||
#define OPCODE_H
|
||||
#include <assert.h>
|
||||
|
||||
typedef enum {
|
||||
#define OP(name, imm, in, out) name,
|
||||
#include "opcode_list.h"
|
||||
#undef OP
|
||||
} opcode;
|
||||
|
||||
enum {
|
||||
NUM_OPCODES =
|
||||
#define OP(name, imm, in, out) +1
|
||||
#include "opcode_list.h"
|
||||
#undef OP
|
||||
};
|
||||
|
||||
enum {
|
||||
OP_HAS_CONSTANT = 2,
|
||||
OP_HAS_VARIABLE = 4,
|
||||
OP_HAS_BRANCH = 8,
|
||||
OP_HAS_CFUNC = 32,
|
||||
OP_HAS_UFUNC = 64,
|
||||
OP_IS_CALL_PSEUDO = 128,
|
||||
OP_HAS_BINDING = 1024,
|
||||
};
|
||||
struct opcode_description {
|
||||
opcode op;
|
||||
const char* name;
|
||||
|
||||
int flags;
|
||||
|
||||
// length in 16-bit units
|
||||
int length;
|
||||
|
||||
int stack_in, stack_out;
|
||||
};
|
||||
|
||||
const struct opcode_description* opcode_describe(opcode op);
|
||||
|
||||
#endif
|
||||
@@ -1,13 +1,18 @@
|
||||
OP(LOADK, CONSTANT, 1, 1)
|
||||
OP(DUP, NONE, 1, 2)
|
||||
OP(DUPN, NONE, 1, 2)
|
||||
OP(DUP2, NONE, 2, 3)
|
||||
OP(POP, NONE, 1, 0)
|
||||
OP(LOADV, VARIABLE, 1, 1)
|
||||
OP(LOADVN, VARIABLE, 1, 1)
|
||||
OP(STOREV, VARIABLE, 1, 0)
|
||||
OP(STORE_GLOBAL, GLOBAL, 0, 0)
|
||||
OP(INDEX, NONE, 2, 1)
|
||||
OP(INDEX_OPT, NONE, 2, 1)
|
||||
OP(EACH, NONE, 1, 1)
|
||||
OP(EACH_OPT, NONE, 1, 1)
|
||||
OP(FORK, BRANCH, 0, 0)
|
||||
OP(FORK_OPT, BRANCH, 0, 0)
|
||||
OP(JUMP, BRANCH, 0, 0)
|
||||
OP(JUMP_F,BRANCH, 1, 0)
|
||||
OP(BACKTRACK, NONE, 0, 0)
|
||||
@@ -25,8 +30,15 @@ OP(CALL_BUILTIN, CFUNC, -1, 1)
|
||||
|
||||
OP(CALL_JQ, UFUNC, 1, 1)
|
||||
OP(RET, NONE, 1, 1)
|
||||
OP(TAIL_CALL_JQ, UFUNC, 1, 1)
|
||||
|
||||
OP(CLOSURE_PARAM, DEFINITION, 0, 0)
|
||||
OP(CLOSURE_REF, CLOSURE_REF_IMM, 0, 0)
|
||||
OP(CLOSURE_CREATE, DEFINITION, 0, 0)
|
||||
OP(CLOSURE_CREATE_C, DEFINITION, 0, 0)
|
||||
|
||||
OP(TOP, NONE, 0, 0)
|
||||
OP(CLOSURE_PARAM_REGULAR, DEFINITION, 0, 0)
|
||||
OP(DEPS, CONSTANT, 0, 0)
|
||||
OP(MODULEMETA, CONSTANT, 0, 0)
|
||||
OP(GENLABEL, NONE, 0, 1)
|
||||
@@ -0,0 +1,193 @@
|
||||
/* A Bison parser, made by GNU Bison 3.0.2. */
|
||||
|
||||
/* Bison interface for Yacc-like parsers in C
|
||||
|
||||
Copyright (C) 1984, 1989-1990, 2000-2013 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
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
/* As a special exception, you may create a larger work that contains
|
||||
part or all of the Bison parser skeleton and distribute that work
|
||||
under terms of your choice, so long as that work isn't itself a
|
||||
parser generator using the skeleton or a modified version thereof
|
||||
as a parser skeleton. Alternatively, if you modify or redistribute
|
||||
the parser skeleton itself, you may (at your option) remove this
|
||||
special exception, which will cause the skeleton and the resulting
|
||||
Bison output files to be licensed under the GNU General Public
|
||||
License without this special exception.
|
||||
|
||||
This special exception was added by the Free Software Foundation in
|
||||
version 2.2 of Bison. */
|
||||
|
||||
#ifndef YY_YY_PARSER_H_INCLUDED
|
||||
# define YY_YY_PARSER_H_INCLUDED
|
||||
/* Debug traces. */
|
||||
#ifndef YYDEBUG
|
||||
# define YYDEBUG 0
|
||||
#endif
|
||||
#if YYDEBUG
|
||||
extern int yydebug;
|
||||
#endif
|
||||
/* "%code requires" blocks. */
|
||||
#line 11 "parser.y" /* yacc.c:1909 */
|
||||
|
||||
#include "locfile.h"
|
||||
struct lexer_param;
|
||||
|
||||
#define YYLTYPE location
|
||||
#define YYLLOC_DEFAULT(Loc, Rhs, N) \
|
||||
do { \
|
||||
if (N) { \
|
||||
(Loc).start = YYRHSLOC(Rhs, 1).start; \
|
||||
(Loc).end = YYRHSLOC(Rhs, N).end; \
|
||||
} else { \
|
||||
(Loc).start = YYRHSLOC(Rhs, 0).end; \
|
||||
(Loc).end = YYRHSLOC(Rhs, 0).end; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#line 61 "parser.h" /* yacc.c:1909 */
|
||||
|
||||
/* Token type. */
|
||||
#ifndef YYTOKENTYPE
|
||||
# define YYTOKENTYPE
|
||||
enum yytokentype
|
||||
{
|
||||
INVALID_CHARACTER = 258,
|
||||
IDENT = 259,
|
||||
FIELD = 260,
|
||||
LITERAL = 261,
|
||||
FORMAT = 262,
|
||||
REC = 263,
|
||||
SETMOD = 264,
|
||||
EQ = 265,
|
||||
NEQ = 266,
|
||||
DEFINEDOR = 267,
|
||||
AS = 268,
|
||||
DEF = 269,
|
||||
MODULE = 270,
|
||||
IMPORT = 271,
|
||||
INCLUDE = 272,
|
||||
IF = 273,
|
||||
THEN = 274,
|
||||
ELSE = 275,
|
||||
ELSE_IF = 276,
|
||||
REDUCE = 277,
|
||||
FOREACH = 278,
|
||||
END = 279,
|
||||
AND = 280,
|
||||
OR = 281,
|
||||
TRY = 282,
|
||||
CATCH = 283,
|
||||
LABEL = 284,
|
||||
BREAK = 285,
|
||||
LOC = 286,
|
||||
SETPIPE = 287,
|
||||
SETPLUS = 288,
|
||||
SETMINUS = 289,
|
||||
SETMULT = 290,
|
||||
SETDIV = 291,
|
||||
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
|
||||
};
|
||||
#endif
|
||||
/* Tokens. */
|
||||
#define INVALID_CHARACTER 258
|
||||
#define IDENT 259
|
||||
#define FIELD 260
|
||||
#define LITERAL 261
|
||||
#define FORMAT 262
|
||||
#define REC 263
|
||||
#define SETMOD 264
|
||||
#define EQ 265
|
||||
#define NEQ 266
|
||||
#define DEFINEDOR 267
|
||||
#define AS 268
|
||||
#define DEF 269
|
||||
#define MODULE 270
|
||||
#define IMPORT 271
|
||||
#define INCLUDE 272
|
||||
#define IF 273
|
||||
#define THEN 274
|
||||
#define ELSE 275
|
||||
#define ELSE_IF 276
|
||||
#define REDUCE 277
|
||||
#define FOREACH 278
|
||||
#define END 279
|
||||
#define AND 280
|
||||
#define OR 281
|
||||
#define TRY 282
|
||||
#define CATCH 283
|
||||
#define LABEL 284
|
||||
#define BREAK 285
|
||||
#define LOC 286
|
||||
#define SETPIPE 287
|
||||
#define SETPLUS 288
|
||||
#define SETMINUS 289
|
||||
#define SETMULT 290
|
||||
#define SETDIV 291
|
||||
#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
|
||||
|
||||
/* Value type. */
|
||||
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
|
||||
typedef union YYSTYPE YYSTYPE;
|
||||
union YYSTYPE
|
||||
{
|
||||
#line 31 "parser.y" /* yacc.c:1909 */
|
||||
|
||||
jv literal;
|
||||
block blk;
|
||||
|
||||
#line 170 "parser.h" /* yacc.c:1909 */
|
||||
};
|
||||
# define YYSTYPE_IS_TRIVIAL 1
|
||||
# define YYSTYPE_IS_DECLARED 1
|
||||
#endif
|
||||
|
||||
/* Location type. */
|
||||
#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED
|
||||
typedef struct YYLTYPE YYLTYPE;
|
||||
struct YYLTYPE
|
||||
{
|
||||
int first_line;
|
||||
int first_column;
|
||||
int last_line;
|
||||
int last_column;
|
||||
};
|
||||
# define YYLTYPE_IS_DECLARED 1
|
||||
# define YYLTYPE_IS_TRIVIAL 1
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
int yyparse (block* answer, int* errors, struct locfile* locations, struct lexer_param* lexer_param_ptr);
|
||||
|
||||
#endif /* !YY_YY_PARSER_H_INCLUDED */
|
||||
@@ -1,16 +1,17 @@
|
||||
%{
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "compile.h"
|
||||
#include "jv_alloc.h"
|
||||
#define YYMALLOC jv_mem_alloc
|
||||
#define YYFREE jv_mem_free
|
||||
|
||||
struct lexer_param;
|
||||
|
||||
%}
|
||||
%code requires {
|
||||
#include "locfile.h"
|
||||
struct lexer_param;
|
||||
|
||||
#define YYLTYPE location
|
||||
#define YYLLOC_DEFAULT(Loc, Rhs, N) \
|
||||
do { \
|
||||
@@ -22,7 +23,7 @@ struct lexer_param;
|
||||
(Loc).end = YYRHSLOC(Rhs, 0).end; \
|
||||
} \
|
||||
} while (0)
|
||||
}
|
||||
}
|
||||
|
||||
%locations
|
||||
%error-verbose
|
||||
@@ -47,21 +48,33 @@ struct lexer_param;
|
||||
|
||||
%token INVALID_CHARACTER
|
||||
%token <literal> IDENT
|
||||
%token <literal> FIELD
|
||||
%token <literal> LITERAL
|
||||
%token <literal> FORMAT
|
||||
%token REC ".."
|
||||
%token SETMOD "%="
|
||||
%token EQ "=="
|
||||
%token NEQ "!="
|
||||
%token DEFINEDOR "//"
|
||||
%token AS "as"
|
||||
%token DEF "def"
|
||||
%token MODULE "module"
|
||||
%token IMPORT "import"
|
||||
%token INCLUDE "include"
|
||||
%token IF "if"
|
||||
%token THEN "then"
|
||||
%token ELSE "else"
|
||||
%token ELSE_IF "elif"
|
||||
%token REDUCE "reduce"
|
||||
%token FOREACH "foreach"
|
||||
%token END "end"
|
||||
%token AND "and"
|
||||
%token OR "or"
|
||||
%token TRY "try"
|
||||
%token CATCH "catch"
|
||||
%token LABEL "label"
|
||||
%token BREAK "break"
|
||||
%token LOC "__loc__"
|
||||
%token SETPIPE "|="
|
||||
%token SETPLUS "+="
|
||||
%token SETMINUS "-="
|
||||
@@ -77,20 +90,31 @@ struct lexer_param;
|
||||
%token QQSTRING_INTERP_END
|
||||
%token QQSTRING_END
|
||||
|
||||
/* revolting hack */
|
||||
%left ';'
|
||||
/* Instead of raising this, find a way to use precedence to resolve
|
||||
* shift-reduce conflicts. */
|
||||
%expect 0
|
||||
|
||||
%precedence FUNCDEF
|
||||
%right '|'
|
||||
%left ','
|
||||
%right "//"
|
||||
%nonassoc '=' SETPIPE SETPLUS SETMINUS SETMULT SETDIV SETDEFINEDOR
|
||||
%nonassoc '=' SETPIPE SETPLUS SETMINUS SETMULT SETDIV SETMOD SETDEFINEDOR
|
||||
%left OR
|
||||
%left AND
|
||||
%nonassoc NEQ EQ '<' '>' LESSEQ GREATEREQ
|
||||
%left '+' '-'
|
||||
%left '*' '/'
|
||||
%left '*' '/' '%'
|
||||
%precedence NONOPT /* non-optional; rules for which a specialized
|
||||
'?' rule should be preferred over Exp '?' */
|
||||
%precedence '?'
|
||||
%precedence "try"
|
||||
%precedence "catch"
|
||||
|
||||
|
||||
%type <blk> Exp Term MkDict MkDictPair ExpD ElseBody QQString FuncDef FuncDefs String
|
||||
%type <blk> Exp Term MkDict MkDictPair ExpD ElseBody QQString
|
||||
%type <blk> FuncDef FuncDefs String Import Imports Param Params
|
||||
%type <blk> Arg Args Module Pattern ArrayPats ObjPats ObjPat
|
||||
%type <literal> Keyword
|
||||
%{
|
||||
#include "lexer.h"
|
||||
struct lexer_param {
|
||||
@@ -103,34 +127,36 @@ struct lexer_param {
|
||||
/*YYERROR*/; \
|
||||
} while (0)
|
||||
|
||||
void yyerror(YYLTYPE* loc, block* answer, int* errors,
|
||||
void yyerror(YYLTYPE* loc, block* answer, int* errors,
|
||||
struct locfile* locations, struct lexer_param* lexer_param_ptr, const char *s){
|
||||
(*errors)++;
|
||||
locfile_locate(locations, *loc, "error: %s", s);
|
||||
if (strstr(s, "unexpected")) {
|
||||
#ifdef WIN32
|
||||
locfile_locate(locations, *loc, "jq: error: %s (Windows cmd shell quoting issues?)", s);
|
||||
#else
|
||||
locfile_locate(locations, *loc, "jq: error: %s (Unix shell quoting issues?)", s);
|
||||
#endif
|
||||
} else {
|
||||
locfile_locate(locations, *loc, "jq: error: %s", s);
|
||||
}
|
||||
}
|
||||
|
||||
int yylex(YYSTYPE* yylval, YYLTYPE* yylloc, block* answer, int* errors,
|
||||
int yylex(YYSTYPE* yylval, YYLTYPE* yylloc, block* answer, int* errors,
|
||||
struct locfile* locations, struct lexer_param* lexer_param_ptr) {
|
||||
yyscan_t lexer = lexer_param_ptr->lexer;
|
||||
while (1) {
|
||||
int tok = jq_yylex(yylval, yylloc, lexer);
|
||||
if (tok == INVALID_CHARACTER) {
|
||||
FAIL(*yylloc, "Invalid character");
|
||||
int tok = jq_yylex(yylval, yylloc, lexer);
|
||||
if ((tok == LITERAL || tok == QQSTRING_TEXT) && !jv_is_valid(yylval->literal)) {
|
||||
jv msg = jv_invalid_get_msg(jv_copy(yylval->literal));
|
||||
if (jv_get_kind(msg) == JV_KIND_STRING) {
|
||||
FAIL(*yylloc, jv_string_value(msg));
|
||||
} else {
|
||||
if ((tok == LITERAL || tok == QQSTRING_TEXT) && !jv_is_valid(yylval->literal)) {
|
||||
jv msg = jv_invalid_get_msg(jv_copy(yylval->literal));
|
||||
if (jv_get_kind(msg) == JV_KIND_STRING) {
|
||||
FAIL(*yylloc, jv_string_value(msg));
|
||||
} else {
|
||||
FAIL(*yylloc, "Invalid literal");
|
||||
}
|
||||
jv_free(msg);
|
||||
jv_free(yylval->literal);
|
||||
yylval->literal = jv_null();
|
||||
}
|
||||
return tok;
|
||||
FAIL(*yylloc, "Invalid literal");
|
||||
}
|
||||
jv_free(msg);
|
||||
jv_free(yylval->literal);
|
||||
yylval->literal = jv_null();
|
||||
}
|
||||
return tok;
|
||||
}
|
||||
|
||||
static block gen_dictpair(block k, block v) {
|
||||
@@ -141,7 +167,11 @@ static block gen_index(block obj, block key) {
|
||||
return BLOCK(gen_subexp(key), obj, gen_op_simple(INDEX));
|
||||
}
|
||||
|
||||
static block gen_slice_index(block obj, block start, block end) {
|
||||
static block gen_index_opt(block obj, block key) {
|
||||
return BLOCK(gen_subexp(key), obj, gen_op_simple(INDEX_OPT));
|
||||
}
|
||||
|
||||
static block gen_slice_index(block obj, block start, block end, opcode idx_op) {
|
||||
block key = BLOCK(gen_subexp(gen_const(jv_object())),
|
||||
gen_subexp(gen_const(jv_string("start"))),
|
||||
gen_subexp(start),
|
||||
@@ -149,16 +179,60 @@ static block gen_slice_index(block obj, block start, block end) {
|
||||
gen_subexp(gen_const(jv_string("end"))),
|
||||
gen_subexp(end),
|
||||
gen_op_simple(INSERT));
|
||||
return BLOCK(key, obj, gen_op_simple(INDEX));
|
||||
return BLOCK(key, obj, gen_op_simple(idx_op));
|
||||
}
|
||||
|
||||
static block constant_fold(block a, block b, int op) {
|
||||
if (!block_is_single(a) || !block_is_const(a) ||
|
||||
!block_is_single(b) || !block_is_const(b))
|
||||
return gen_noop();
|
||||
if (block_const_kind(a) != block_const_kind(b))
|
||||
return gen_noop();
|
||||
|
||||
jv res = jv_invalid();
|
||||
|
||||
if (block_const_kind(a) == JV_KIND_NUMBER) {
|
||||
double na = jv_number_value(block_const(a));
|
||||
double nb = jv_number_value(block_const(b));
|
||||
switch (op) {
|
||||
case '+': res = jv_number(na + nb); break;
|
||||
case '-': res = jv_number(na - nb); break;
|
||||
case '*': res = jv_number(na * nb); break;
|
||||
case '/': res = jv_number(na / nb); break;
|
||||
case EQ: res = (na == nb ? jv_true() : jv_false()); break;
|
||||
case NEQ: res = (na != nb ? jv_true() : jv_false()); break;
|
||||
case '<': res = (na < nb ? jv_true() : jv_false()); break;
|
||||
case '>': res = (na > nb ? jv_true() : jv_false()); break;
|
||||
case LESSEQ: res = (na <= nb ? jv_true() : jv_false()); break;
|
||||
case GREATEREQ: res = (na >= nb ? jv_true() : jv_false()); break;
|
||||
default: break;
|
||||
}
|
||||
} else if (op == '+' && block_const_kind(a) == JV_KIND_STRING) {
|
||||
res = jv_string_concat(block_const(a), block_const(b));
|
||||
} else {
|
||||
return gen_noop();
|
||||
}
|
||||
|
||||
if (jv_get_kind(res) == JV_KIND_INVALID)
|
||||
return gen_noop();
|
||||
|
||||
block_free(a);
|
||||
block_free(b);
|
||||
return gen_const(res);
|
||||
}
|
||||
|
||||
static block gen_binop(block a, block b, int op) {
|
||||
block folded = constant_fold(a, b, op);
|
||||
if (!block_is_noop(folded))
|
||||
return folded;
|
||||
|
||||
const char* funcname = 0;
|
||||
switch (op) {
|
||||
case '+': funcname = "_plus"; break;
|
||||
case '-': funcname = "_minus"; break;
|
||||
case '*': funcname = "_multiply"; break;
|
||||
case '/': funcname = "_divide"; break;
|
||||
case '%': funcname = "_mod"; break;
|
||||
case EQ: funcname = "_equal"; break;
|
||||
case NEQ: funcname = "_notequal"; break;
|
||||
case '<': funcname = "_less"; break;
|
||||
@@ -176,24 +250,22 @@ static block gen_format(block a, jv fmt) {
|
||||
}
|
||||
|
||||
static block gen_definedor_assign(block object, block val) {
|
||||
block tmp = block_bind(gen_op_var_unbound(STOREV, "tmp"),
|
||||
gen_noop(), OP_HAS_VARIABLE);
|
||||
block tmp = gen_op_var_fresh(STOREV, "tmp");
|
||||
return BLOCK(gen_op_simple(DUP),
|
||||
val, tmp,
|
||||
gen_call("_modify", BLOCK(gen_lambda(object),
|
||||
gen_lambda(gen_definedor(gen_noop(),
|
||||
gen_op_var_bound(LOADV, tmp))))));
|
||||
gen_lambda(gen_definedor(gen_noop(),
|
||||
gen_op_bound(LOADV, tmp))))));
|
||||
}
|
||||
|
||||
|
||||
static block gen_update(block object, block val, int optype) {
|
||||
block tmp = block_bind(gen_op_var_unbound(STOREV, "tmp"),
|
||||
gen_noop(), OP_HAS_VARIABLE);
|
||||
block tmp = gen_op_var_fresh(STOREV, "tmp");
|
||||
return BLOCK(gen_op_simple(DUP),
|
||||
val,
|
||||
tmp,
|
||||
gen_call("_modify", BLOCK(gen_lambda(object),
|
||||
gen_call("_modify", BLOCK(gen_lambda(object),
|
||||
gen_lambda(gen_binop(gen_noop(),
|
||||
gen_op_var_bound(LOADV, tmp),
|
||||
gen_op_bound(LOADV, tmp),
|
||||
optype)))));
|
||||
}
|
||||
|
||||
@@ -201,51 +273,102 @@ static block gen_update(block object, block val, int optype) {
|
||||
|
||||
%%
|
||||
TopLevel:
|
||||
Exp {
|
||||
*answer = $1;
|
||||
Module Imports Exp {
|
||||
*answer = BLOCK($1, $2, gen_op_simple(TOP), $3);
|
||||
} |
|
||||
FuncDefs {
|
||||
*answer = $1;
|
||||
}
|
||||
Module Imports FuncDefs {
|
||||
*answer = BLOCK($1, $2, $3);
|
||||
}
|
||||
|
||||
Module:
|
||||
%empty {
|
||||
$$ = gen_noop();
|
||||
} |
|
||||
"module" Exp ';' {
|
||||
if (!block_is_const($2)) {
|
||||
FAIL(@$, "Module metadata must be constant.");
|
||||
$$ = gen_noop();
|
||||
} else {
|
||||
$$ = gen_module($2);
|
||||
}
|
||||
}
|
||||
|
||||
Imports:
|
||||
%empty {
|
||||
$$ = gen_noop();
|
||||
} |
|
||||
Import Imports {
|
||||
$$ = BLOCK($1, $2);
|
||||
}
|
||||
|
||||
FuncDefs:
|
||||
/* empty */ {
|
||||
%empty {
|
||||
$$ = gen_noop();
|
||||
} |
|
||||
FuncDef FuncDefs {
|
||||
$$ = block_join($1, $2);
|
||||
$$ = block_bind($1, $2, OP_IS_CALL_PSEUDO);
|
||||
}
|
||||
|
||||
Exp:
|
||||
FuncDef Exp %prec ';' {
|
||||
$$ = block_bind($1, $2, OP_IS_CALL_PSEUDO);
|
||||
FuncDef Exp %prec FUNCDEF {
|
||||
$$ = block_bind_referenced($1, $2, OP_IS_CALL_PSEUDO);
|
||||
} |
|
||||
|
||||
Term "as" '$' IDENT '|' Exp {
|
||||
$$ = gen_var_binding($1, jv_string_value($4), $6);
|
||||
jv_free($4);
|
||||
Term "as" Pattern '|' Exp {
|
||||
$$ = gen_destructure($1, $3, $5);
|
||||
} |
|
||||
|
||||
"reduce" Term "as" '$' IDENT '(' Exp ';' Exp ')' {
|
||||
$$ = gen_reduce(jv_string_value($5), $2, $7, $9);
|
||||
jv_free($5);
|
||||
"reduce" Term "as" Pattern '(' Exp ';' Exp ')' {
|
||||
$$ = gen_reduce($2, $4, $6, $8);
|
||||
} |
|
||||
|
||||
"foreach" Term "as" Pattern '(' Exp ';' Exp ';' Exp ')' {
|
||||
$$ = gen_foreach($2, $4, $6, $8, $10);
|
||||
} |
|
||||
|
||||
"foreach" Term "as" Pattern '(' Exp ';' Exp ')' {
|
||||
$$ = gen_foreach($2, $4, $6, $8, gen_noop());
|
||||
} |
|
||||
|
||||
"if" Exp "then" Exp ElseBody {
|
||||
$$ = gen_cond($2, $4, $5);
|
||||
} |
|
||||
"if" Exp error {
|
||||
FAIL(@$, "Possibly unterminated 'if' statment");
|
||||
"if" Exp "then" error {
|
||||
FAIL(@$, "Possibly unterminated 'if' statement");
|
||||
$$ = $2;
|
||||
} |
|
||||
|
||||
"try" Exp "catch" Exp {
|
||||
//$$ = BLOCK(gen_op_target(FORK_OPT, $2), $2, $4);
|
||||
$$ = gen_try($2, gen_try_handler($4));
|
||||
} |
|
||||
"try" Exp {
|
||||
//$$ = BLOCK(gen_op_target(FORK_OPT, $2), $2, gen_op_simple(BACKTRACK));
|
||||
$$ = gen_try($2, gen_op_simple(BACKTRACK));
|
||||
} |
|
||||
"try" Exp "catch" error {
|
||||
FAIL(@$, "Possibly unterminated 'try' statement");
|
||||
$$ = $2;
|
||||
} |
|
||||
|
||||
"label" '$' IDENT '|' Exp {
|
||||
jv v = jv_string_fmt("*label-%s", jv_string_value($3));
|
||||
$$ = gen_location(@$, locations, gen_label(jv_string_value(v), $5));
|
||||
jv_free($3);
|
||||
jv_free(v);
|
||||
} |
|
||||
|
||||
Exp '?' {
|
||||
$$ = gen_try($1, gen_op_simple(BACKTRACK));
|
||||
} |
|
||||
|
||||
Exp '=' Exp {
|
||||
$$ = gen_call("_assign", BLOCK(gen_lambda($1), gen_lambda($3)));
|
||||
} |
|
||||
|
||||
Exp "or" Exp {
|
||||
$$ = gen_or($1, $3);
|
||||
} |
|
||||
} |
|
||||
|
||||
Exp "and" Exp {
|
||||
$$ = gen_and($1, $3);
|
||||
@@ -263,12 +386,12 @@ Exp "|=" Exp {
|
||||
$$ = gen_call("_modify", BLOCK(gen_lambda($1), gen_lambda($3)));
|
||||
} |
|
||||
|
||||
Exp '|' Exp {
|
||||
$$ = block_join($1, $3);
|
||||
Exp '|' Exp {
|
||||
$$ = block_join($1, $3);
|
||||
} |
|
||||
|
||||
Exp ',' Exp {
|
||||
$$ = gen_both($1, $3);
|
||||
Exp ',' Exp {
|
||||
$$ = gen_both($1, $3);
|
||||
} |
|
||||
|
||||
Exp '+' Exp {
|
||||
@@ -301,12 +424,24 @@ Exp "*=" Exp {
|
||||
|
||||
Exp '/' Exp {
|
||||
$$ = gen_binop($1, $3, '/');
|
||||
if (block_is_const_inf($$))
|
||||
FAIL(@$, "Division by zero?");
|
||||
} |
|
||||
|
||||
Exp '%' Exp {
|
||||
$$ = gen_binop($1, $3, '%');
|
||||
if (block_is_const_inf($$))
|
||||
FAIL(@$, "Remainder by zero?");
|
||||
} |
|
||||
|
||||
Exp "/=" Exp {
|
||||
$$ = gen_update($1, $3, '/');
|
||||
} |
|
||||
|
||||
Exp SETMOD Exp {
|
||||
$$ = gen_update($1, $3, '%');
|
||||
} |
|
||||
|
||||
Exp "==" Exp {
|
||||
$$ = gen_binop($1, $3, EQ);
|
||||
} |
|
||||
@@ -331,8 +466,67 @@ Exp ">=" Exp {
|
||||
$$ = gen_binop($1, $3, GREATEREQ);
|
||||
} |
|
||||
|
||||
Term {
|
||||
$$ = $1;
|
||||
Term {
|
||||
$$ = $1;
|
||||
}
|
||||
|
||||
Import:
|
||||
"import" String "as" '$' IDENT ';' {
|
||||
jv v = block_const($2);
|
||||
// XXX Make gen_import take only blocks and the int is_data so we
|
||||
// don't have to free so much stuff here
|
||||
$$ = gen_import(jv_string_value(v), gen_noop(), jv_string_value($5), 1);
|
||||
block_free($2);
|
||||
jv_free($5);
|
||||
jv_free(v);
|
||||
} |
|
||||
"import" String "as" IDENT ';' {
|
||||
jv v = block_const($2);
|
||||
$$ = gen_import(jv_string_value(v), gen_noop(), jv_string_value($4), 0);
|
||||
block_free($2);
|
||||
jv_free($4);
|
||||
jv_free(v);
|
||||
} |
|
||||
"include" String ';' {
|
||||
jv v = block_const($2);
|
||||
$$ = gen_import(jv_string_value(v), gen_noop(), NULL, 0);
|
||||
block_free($2);
|
||||
jv_free(v);
|
||||
} |
|
||||
"import" String "as" IDENT Exp ';' {
|
||||
if (!block_is_const($5)) {
|
||||
FAIL(@$, "Module metadata must be constant.");
|
||||
$$ = gen_noop();
|
||||
} else {
|
||||
jv v = block_const($2);
|
||||
$$ = gen_import(jv_string_value(v), $5, jv_string_value($4), 0);
|
||||
jv_free(v);
|
||||
}
|
||||
block_free($2);
|
||||
jv_free($4);
|
||||
} |
|
||||
"include" String Exp ';' {
|
||||
if (!block_is_const($3)) {
|
||||
FAIL(@$, "Module metadata must be constant.");
|
||||
$$ = gen_noop();
|
||||
} else {
|
||||
jv v = block_const($2);
|
||||
$$ = gen_import(jv_string_value(v), $3, NULL, 0);
|
||||
jv_free(v);
|
||||
}
|
||||
block_free($2);
|
||||
} |
|
||||
"import" String "as" '$' IDENT Exp ';' {
|
||||
if (!block_is_const($6)) {
|
||||
FAIL(@$, "Module metadata must be constant.");
|
||||
$$ = gen_noop();
|
||||
} else {
|
||||
jv v = block_const($2);
|
||||
$$ = gen_import(jv_string_value(v), $6, jv_string_value($5), 1);
|
||||
jv_free(v);
|
||||
}
|
||||
block_free($2);
|
||||
jv_free($5);
|
||||
}
|
||||
|
||||
FuncDef:
|
||||
@@ -341,24 +535,29 @@ FuncDef:
|
||||
jv_free($2);
|
||||
} |
|
||||
|
||||
"def" IDENT '(' IDENT ')' ':' Exp ';' {
|
||||
$$ = gen_function(jv_string_value($2),
|
||||
gen_op_block_unbound(CLOSURE_PARAM, jv_string_value($4)),
|
||||
$7);
|
||||
"def" IDENT '(' Params ')' ':' Exp ';' {
|
||||
$$ = gen_function(jv_string_value($2), $4, $7);
|
||||
jv_free($2);
|
||||
jv_free($4);
|
||||
} |
|
||||
|
||||
"def" IDENT '(' IDENT ';' IDENT ')' ':' Exp ';' {
|
||||
$$ = gen_function(jv_string_value($2),
|
||||
BLOCK(gen_op_block_unbound(CLOSURE_PARAM, jv_string_value($4)),
|
||||
gen_op_block_unbound(CLOSURE_PARAM, jv_string_value($6))),
|
||||
$9);
|
||||
jv_free($2);
|
||||
jv_free($4);
|
||||
jv_free($6);
|
||||
}
|
||||
|
||||
Params:
|
||||
Param {
|
||||
$$ = $1;
|
||||
} |
|
||||
Params ';' Param {
|
||||
$$ = BLOCK($1, $3);
|
||||
}
|
||||
|
||||
Param:
|
||||
'$' IDENT {
|
||||
$$ = gen_param_regular(jv_string_value($2));
|
||||
jv_free($2);
|
||||
} |
|
||||
|
||||
IDENT {
|
||||
$$ = gen_param(jv_string_value($1));
|
||||
jv_free($1);
|
||||
}
|
||||
|
||||
|
||||
String:
|
||||
@@ -373,7 +572,7 @@ FORMAT QQSTRING_START { $<literal>$ = $1; } QQString QQSTRING_END {
|
||||
|
||||
|
||||
QQString:
|
||||
/* empty */ {
|
||||
%empty {
|
||||
$$ = gen_const(jv_string(""));
|
||||
} |
|
||||
QQString QQSTRING_TEXT {
|
||||
@@ -393,10 +592,12 @@ ElseBody:
|
||||
}
|
||||
|
||||
ExpD:
|
||||
ExpD '|' ExpD {
|
||||
ExpD '|' ExpD {
|
||||
$$ = block_join($1, $3);
|
||||
} |
|
||||
|
||||
'-' ExpD {
|
||||
$$ = BLOCK($2, gen_call("_negate", gen_noop()));
|
||||
} |
|
||||
Term {
|
||||
$$ = $1;
|
||||
}
|
||||
@@ -404,32 +605,89 @@ Term {
|
||||
|
||||
Term:
|
||||
'.' {
|
||||
$$ = gen_noop();
|
||||
$$ = gen_noop();
|
||||
} |
|
||||
Term '.' IDENT {
|
||||
$$ = gen_index($1, gen_const($3));
|
||||
REC {
|
||||
$$ = gen_call("recurse", gen_noop());
|
||||
} |
|
||||
'.' IDENT {
|
||||
$$ = gen_index(gen_noop(), gen_const($2));
|
||||
BREAK '$' IDENT {
|
||||
jv v = jv_string_fmt("*label-%s", jv_string_value($3)); // impossible symbol
|
||||
$$ = gen_location(@$, locations,
|
||||
BLOCK(gen_op_unbound(LOADV, jv_string_value(v)),
|
||||
gen_call("error", gen_noop())));
|
||||
jv_free(v);
|
||||
jv_free($3);
|
||||
} |
|
||||
BREAK error {
|
||||
FAIL(@$, "break requires a label to break to");
|
||||
$$ = gen_noop();
|
||||
} |
|
||||
Term FIELD '?' {
|
||||
$$ = gen_index_opt($1, gen_const($2));
|
||||
} |
|
||||
FIELD '?' {
|
||||
$$ = gen_index_opt(gen_noop(), gen_const($1));
|
||||
} |
|
||||
Term '.' String '?' {
|
||||
$$ = gen_index_opt($1, $3);
|
||||
} |
|
||||
'.' String '?' {
|
||||
$$ = gen_index_opt(gen_noop(), $2);
|
||||
} |
|
||||
Term FIELD %prec NONOPT {
|
||||
$$ = gen_index($1, gen_const($2));
|
||||
} |
|
||||
FIELD %prec NONOPT {
|
||||
$$ = gen_index(gen_noop(), gen_const($1));
|
||||
} |
|
||||
Term '.' String %prec NONOPT {
|
||||
$$ = gen_index($1, $3);
|
||||
} |
|
||||
'.' String %prec NONOPT {
|
||||
$$ = gen_index(gen_noop(), $2);
|
||||
} |
|
||||
'.' error {
|
||||
FAIL(@$, "try .[\"field\"] instead of .field for unusually named fields");
|
||||
$$ = gen_noop();
|
||||
} |
|
||||
'.' IDENT error {
|
||||
jv_free($2);
|
||||
FAIL(@$, "try .[\"field\"] instead of .field for unusually named fields");
|
||||
$$ = gen_noop();
|
||||
} |
|
||||
/* FIXME: string literals */
|
||||
Term '[' Exp ']' {
|
||||
$$ = gen_index($1, $3);
|
||||
Term '[' Exp ']' '?' {
|
||||
$$ = gen_index_opt($1, $3);
|
||||
} |
|
||||
Term '[' ']' {
|
||||
$$ = block_join($1, gen_op_simple(EACH));
|
||||
Term '[' Exp ']' %prec NONOPT {
|
||||
$$ = gen_index($1, $3);
|
||||
} |
|
||||
Term '[' Exp ':' Exp ']' {
|
||||
$$ = gen_slice_index($1, $3, $5);
|
||||
Term '[' ']' '?' {
|
||||
$$ = block_join($1, gen_op_simple(EACH_OPT));
|
||||
} |
|
||||
Term '[' Exp ':' ']' {
|
||||
$$ = gen_slice_index($1, $3, gen_const(jv_null()));
|
||||
Term '[' ']' %prec NONOPT {
|
||||
$$ = block_join($1, gen_op_simple(EACH));
|
||||
} |
|
||||
Term '[' ':' Exp ']' {
|
||||
$$ = gen_slice_index($1, gen_const(jv_null()), $4);
|
||||
Term '[' Exp ':' Exp ']' '?' {
|
||||
$$ = gen_slice_index($1, $3, $5, INDEX_OPT);
|
||||
} |
|
||||
Term '[' Exp ':' ']' '?' {
|
||||
$$ = gen_slice_index($1, $3, gen_const(jv_null()), INDEX_OPT);
|
||||
} |
|
||||
Term '[' ':' Exp ']' '?' {
|
||||
$$ = gen_slice_index($1, gen_const(jv_null()), $4, INDEX_OPT);
|
||||
} |
|
||||
Term '[' Exp ':' Exp ']' %prec NONOPT {
|
||||
$$ = gen_slice_index($1, $3, $5, INDEX);
|
||||
} |
|
||||
Term '[' Exp ':' ']' %prec NONOPT {
|
||||
$$ = gen_slice_index($1, $3, gen_const(jv_null()), INDEX);
|
||||
} |
|
||||
Term '[' ':' Exp ']' %prec NONOPT {
|
||||
$$ = gen_slice_index($1, gen_const(jv_null()), $4, INDEX);
|
||||
} |
|
||||
LITERAL {
|
||||
$$ = gen_const($1);
|
||||
$$ = gen_const($1);
|
||||
} |
|
||||
String {
|
||||
$$ = $1;
|
||||
@@ -437,34 +695,45 @@ String {
|
||||
FORMAT {
|
||||
$$ = gen_format(gen_noop(), $1);
|
||||
} |
|
||||
'(' Exp ')' {
|
||||
$$ = $2;
|
||||
} |
|
||||
'[' Exp ']' {
|
||||
$$ = gen_collect($2);
|
||||
'(' Exp ')' {
|
||||
$$ = $2;
|
||||
} |
|
||||
'[' ']' {
|
||||
$$ = gen_const(jv_array());
|
||||
'[' Exp ']' {
|
||||
$$ = gen_collect($2);
|
||||
} |
|
||||
'{' MkDict '}' {
|
||||
$$ = BLOCK(gen_subexp(gen_const(jv_object())), $2, gen_op_simple(POP));
|
||||
'[' ']' {
|
||||
$$ = gen_const(jv_array());
|
||||
} |
|
||||
'{' MkDict '}' {
|
||||
block o = gen_const_object($2);
|
||||
if (o.first != NULL)
|
||||
$$ = o;
|
||||
else
|
||||
$$ = BLOCK(gen_subexp(gen_const(jv_object())), $2, gen_op_simple(POP));
|
||||
} |
|
||||
'$' LOC {
|
||||
$$ = gen_const(JV_OBJECT(jv_string("file"), jv_copy(locations->fname),
|
||||
jv_string("line"), jv_number(locfile_get_line(locations, @$.start) + 1)));
|
||||
} |
|
||||
'$' IDENT {
|
||||
$$ = gen_location(@$, gen_op_var_unbound(LOADV, jv_string_value($2)));
|
||||
$$ = gen_location(@$, locations, gen_op_unbound(LOADV, jv_string_value($2)));
|
||||
jv_free($2);
|
||||
} |
|
||||
} |
|
||||
IDENT {
|
||||
$$ = gen_location(@$, gen_call(jv_string_value($1), gen_noop()));
|
||||
const char *s = jv_string_value($1);
|
||||
if (strcmp(s, "false") == 0)
|
||||
$$ = gen_const(jv_false());
|
||||
else if (strcmp(s, "true") == 0)
|
||||
$$ = gen_const(jv_true());
|
||||
else if (strcmp(s, "null") == 0)
|
||||
$$ = gen_const(jv_null());
|
||||
else
|
||||
$$ = gen_location(@$, locations, gen_call(s, gen_noop()));
|
||||
jv_free($1);
|
||||
} |
|
||||
IDENT '(' Exp ')' {
|
||||
$$ = gen_call(jv_string_value($1), gen_lambda($3));
|
||||
$$ = gen_location(@1, $$);
|
||||
jv_free($1);
|
||||
} |
|
||||
IDENT '(' Exp ';' Exp ')' {
|
||||
$$ = gen_call(jv_string_value($1), BLOCK(gen_lambda($3), gen_lambda($5)));
|
||||
$$ = gen_location(@1, $$);
|
||||
IDENT '(' Args ')' {
|
||||
$$ = gen_call(jv_string_value($1), $3);
|
||||
$$ = gen_location(@1, locations, $$);
|
||||
jv_free($1);
|
||||
} |
|
||||
'(' error ')' { $$ = gen_noop(); } |
|
||||
@@ -472,18 +741,138 @@ IDENT '(' Exp ';' Exp ')' {
|
||||
Term '[' error ']' { $$ = $1; } |
|
||||
'{' error '}' { $$ = gen_noop(); }
|
||||
|
||||
Args:
|
||||
Arg {
|
||||
$$ = $1;
|
||||
} |
|
||||
Args ';' Arg {
|
||||
$$ = BLOCK($1, $3);
|
||||
}
|
||||
|
||||
Arg:
|
||||
Exp {
|
||||
$$ = gen_lambda($1);
|
||||
}
|
||||
|
||||
Pattern:
|
||||
'$' IDENT {
|
||||
$$ = gen_op_unbound(STOREV, jv_string_value($2));
|
||||
jv_free($2);
|
||||
} |
|
||||
'[' ArrayPats ']' {
|
||||
$$ = BLOCK($2, gen_op_simple(POP));
|
||||
} |
|
||||
'{' ObjPats '}' {
|
||||
$$ = BLOCK($2, gen_op_simple(POP));
|
||||
}
|
||||
|
||||
ArrayPats:
|
||||
Pattern {
|
||||
$$ = gen_array_matcher(gen_noop(), $1);
|
||||
} |
|
||||
ArrayPats ',' Pattern {
|
||||
$$ = gen_array_matcher($1, $3);
|
||||
}
|
||||
|
||||
ObjPats:
|
||||
ObjPat {
|
||||
$$ = $1;
|
||||
} |
|
||||
ObjPats ',' ObjPat {
|
||||
$$ = BLOCK($1, $3);
|
||||
}
|
||||
|
||||
ObjPat:
|
||||
'$' IDENT {
|
||||
$$ = gen_object_matcher(gen_const($2), gen_op_unbound(STOREV, jv_string_value($2)));
|
||||
} |
|
||||
IDENT ':' Pattern {
|
||||
$$ = gen_object_matcher(gen_const($1), $3);
|
||||
} |
|
||||
Keyword ':' Pattern {
|
||||
$$ = gen_object_matcher(gen_const($1), $3);
|
||||
} |
|
||||
String ':' Pattern {
|
||||
$$ = gen_object_matcher($1, $3);
|
||||
} |
|
||||
'(' Exp ')' ':' Pattern {
|
||||
$$ = gen_object_matcher($2, $5);
|
||||
}
|
||||
|
||||
Keyword:
|
||||
"as" {
|
||||
$$ = jv_string("as");
|
||||
} |
|
||||
"def" {
|
||||
$$ = jv_string("def");
|
||||
} |
|
||||
"module" {
|
||||
$$ = jv_string("module");
|
||||
} |
|
||||
"import" {
|
||||
$$ = jv_string("import");
|
||||
} |
|
||||
"include" {
|
||||
$$ = jv_string("include");
|
||||
} |
|
||||
"if" {
|
||||
$$ = jv_string("if");
|
||||
} |
|
||||
"then" {
|
||||
$$ = jv_string("then");
|
||||
} |
|
||||
"else" {
|
||||
$$ = jv_string("else");
|
||||
} |
|
||||
"elif" {
|
||||
$$ = jv_string("elif");
|
||||
} |
|
||||
"reduce" {
|
||||
$$ = jv_string("reduce");
|
||||
} |
|
||||
"foreach" {
|
||||
$$ = jv_string("foreach");
|
||||
} |
|
||||
"end" {
|
||||
$$ = jv_string("end");
|
||||
} |
|
||||
"and" {
|
||||
$$ = jv_string("and");
|
||||
} |
|
||||
"or" {
|
||||
$$ = jv_string("or");
|
||||
} |
|
||||
"try" {
|
||||
$$ = jv_string("try");
|
||||
} |
|
||||
"catch" {
|
||||
$$ = jv_string("catch");
|
||||
} |
|
||||
"label" {
|
||||
$$ = jv_string("label");
|
||||
} |
|
||||
"break" {
|
||||
$$ = jv_string("break");
|
||||
} |
|
||||
"__loc__" {
|
||||
$$ = jv_string("__loc__");
|
||||
}
|
||||
|
||||
MkDict:
|
||||
{
|
||||
$$=gen_noop();
|
||||
%empty {
|
||||
$$=gen_noop();
|
||||
} |
|
||||
MkDictPair { $$ = $1; }
|
||||
| MkDictPair ',' MkDict { $$=block_join($1, $3); }
|
||||
| error ',' MkDict { $$ = $3; }
|
||||
|
||||
MkDictPair
|
||||
: IDENT ':' ExpD {
|
||||
MkDictPair:
|
||||
IDENT ':' ExpD {
|
||||
$$ = gen_dictpair(gen_const($1), $3);
|
||||
}
|
||||
| Keyword ':' ExpD {
|
||||
$$ = gen_dictpair(gen_const($1), $3);
|
||||
}
|
||||
| String ':' ExpD {
|
||||
$$ = gen_dictpair($1, $3);
|
||||
}
|
||||
@@ -491,6 +880,10 @@ MkDictPair
|
||||
$$ = gen_dictpair($1, BLOCK(gen_op_simple(POP), gen_op_simple(DUP2),
|
||||
gen_op_simple(DUP2), gen_op_simple(INDEX)));
|
||||
}
|
||||
| '$' IDENT {
|
||||
$$ = gen_dictpair(gen_const($2),
|
||||
gen_location(@$, locations, gen_op_unbound(LOADV, jv_string_value($2))));
|
||||
}
|
||||
| IDENT {
|
||||
$$ = gen_dictpair(gen_const(jv_copy($1)),
|
||||
gen_index(gen_noop(), gen_const($1)));
|
||||
@@ -521,9 +914,10 @@ int jq_parse(struct locfile* locations, block* answer) {
|
||||
int jq_parse_library(struct locfile* locations, block* answer) {
|
||||
int errs = jq_parse(locations, answer);
|
||||
if (errs) return errs;
|
||||
if (!block_has_only_binders(*answer, OP_IS_CALL_PSEUDO)) {
|
||||
locfile_locate(locations, UNKNOWN_LOCATION, "error: library should only have function definitions, not a main expression");
|
||||
if (block_has_main(*answer)) {
|
||||
locfile_locate(locations, UNKNOWN_LOCATION, "jq: error: library should only have function definitions, not a main expression");
|
||||
return 1;
|
||||
}
|
||||
assert(block_has_only_binders_and_imports(*answer, OP_IS_CALL_PSEUDO));
|
||||
return 0;
|
||||
}
|
||||
+19
-8
@@ -13,19 +13,30 @@
|
||||
set -e
|
||||
cd `dirname "$0"`/../build
|
||||
|
||||
jobs=-j4
|
||||
case "X$1" in
|
||||
X-j*) jobs=$1; shift;;
|
||||
esac
|
||||
|
||||
plat="$1"
|
||||
[ -z "$plat" ] && exit 1
|
||||
shift
|
||||
|
||||
case "$plat" in
|
||||
*/*) echo "platform name must not be a path"; exit 1;;
|
||||
*..*) echo "platform name must not be a path"; exit 1;;
|
||||
*) plat=$PWD/$plat;;
|
||||
esac
|
||||
|
||||
[ -d "$plat" ] || mkdir "$plat"
|
||||
rm -rf "$plat/tmp"
|
||||
mkdir "$plat/tmp"
|
||||
cd "$plat/tmp"
|
||||
mkdir install_other
|
||||
../../../configure \
|
||||
--prefix="`pwd`/install_other" \
|
||||
--bindir="`pwd`/.." \
|
||||
"$@"
|
||||
make install-binaries
|
||||
cd ..
|
||||
rm -rf tmp
|
||||
../../../configure "$@"
|
||||
make "$jobs" DESTDIR=$plat install
|
||||
set -x
|
||||
for jq in `find . -type f \( -name jq -o -name jq.exe \) -print`; do
|
||||
cp "$jq" ..
|
||||
done
|
||||
rm -rf tmp
|
||||
|
||||
@@ -6,13 +6,12 @@ mask = lambda n: (1 << n) - 1
|
||||
|
||||
|
||||
def print_table(type, name, t):
|
||||
assert len(t) == 256
|
||||
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]) +
|
||||
("," if i + 16 < 256 else "};"))
|
||||
("," if i + 16 < len(t) else "};"))
|
||||
|
||||
|
||||
def utf8info(c):
|
||||
@@ -30,3 +29,4 @@ print("#define UTF8_CONTINUATION_BYTE ((unsigned char)255)")
|
||||
|
||||
print_table("unsigned char", "utf8_coding_length", table(0))
|
||||
print_table("unsigned char", "utf8_coding_bits", table(1))
|
||||
print_table("int", "utf8_first_codepoint", [0, 0x0, 0x80, 0x800, 0x10000])
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/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-//'
|
||||
@@ -9,12 +9,15 @@
|
||||
|
||||
if [ "clean" == "$1" ]; then
|
||||
make distclean || echo "continuing..."
|
||||
rm -rf tmp autom4te.cache config
|
||||
rm -f INSTALL Makefile.in aclocal.m4 configure config.h.in ChangeLog
|
||||
rm -f jv_utf8_tables.gen.h lexer.c lexer.h parser.c parser.h
|
||||
find config/m4 -type f \( -name libtool\* -o -name lt\* \) | xargs rm -f
|
||||
find config -name m4 -prune \
|
||||
-o -name .gitignore -prune \
|
||||
-o -type f -print | xargs rm -f
|
||||
rm -rf autom4te.cache
|
||||
rm -f Makefile.in aclocal.m4 configure
|
||||
elif [ "superclean" == "$1" ]; then
|
||||
# if autoconf errors during distcheck, it leaves files that need chmod'ing
|
||||
ver=$(tr -d '\n' <VERSION)
|
||||
ver=$(scripts/version|tr -d '\n')
|
||||
if [ "x${ver}" != "x" ]; then
|
||||
if [ -d jq-${ver} ]; then
|
||||
chmod -R u+w jq-${ver}
|
||||
@@ -24,9 +27,7 @@ elif [ "superclean" == "$1" ]; then
|
||||
fi
|
||||
else
|
||||
autoreconf --install
|
||||
./configure --prefix=/opt/junk
|
||||
make check
|
||||
mkdir tmp
|
||||
make DESTDIR=./tmp install
|
||||
make distcheck
|
||||
./configure --disable-valgrind
|
||||
make -j8
|
||||
make check -j4
|
||||
fi
|
||||
-602
@@ -1,602 +0,0 @@
|
||||
# Tests are groups of three lines: program, input, expected output
|
||||
# Blank lines and lines starting with # are ignored
|
||||
|
||||
#
|
||||
# Simple value tests to check parser. Input is irrelevant
|
||||
#
|
||||
|
||||
true
|
||||
null
|
||||
true
|
||||
|
||||
false
|
||||
null
|
||||
false
|
||||
|
||||
null
|
||||
42
|
||||
null
|
||||
|
||||
1
|
||||
null
|
||||
1
|
||||
|
||||
|
||||
-1
|
||||
null
|
||||
-1
|
||||
|
||||
# FIXME: much more number testing needed
|
||||
|
||||
{}
|
||||
null
|
||||
{}
|
||||
|
||||
[]
|
||||
null
|
||||
[]
|
||||
|
||||
# The input line starts with a 0xFEFF (byte order mark) codepoint
|
||||
# No, there is no reason to have a byte order mark in UTF8 text.
|
||||
# But apparently people do, so jq shouldn't break on it.
|
||||
.
|
||||
"byte order mark"
|
||||
"byte order mark"
|
||||
|
||||
# We test escapes by matching them against Unicode codepoints
|
||||
# FIXME: more tests needed for weird unicode stuff (e.g. utf16 pairs)
|
||||
"Aa\r\n\t\b\f\u03bc"
|
||||
null
|
||||
"Aa\u000d\u000a\u0009\u0008\u000c\u03bc"
|
||||
|
||||
.
|
||||
"Aa\r\n\t\b\f\u03bc"
|
||||
"Aa\u000d\u000a\u0009\u0008\u000c\u03bc"
|
||||
|
||||
"inter\("pol" + "ation")"
|
||||
null
|
||||
"interpolation"
|
||||
|
||||
@text,@json,([1,.] | @csv),@html,@uri,@sh,@base64
|
||||
"<>&'\""
|
||||
"<>&'\""
|
||||
"\"<>&'\\\"\""
|
||||
"1,\"<>&'\"\"\""
|
||||
"<>&'""
|
||||
"%3c%3e%26'%22"
|
||||
"'<>&'\\''\"'"
|
||||
"PD4mJyI="
|
||||
|
||||
@uri
|
||||
"\u03bc"
|
||||
"%ce%bc"
|
||||
|
||||
@html "<b>\(.)</b>"
|
||||
"<script>hax</script>"
|
||||
"<b><script>hax</script></b>"
|
||||
|
||||
#
|
||||
# Dictionary construction syntax
|
||||
#
|
||||
|
||||
{a: 1}
|
||||
null
|
||||
{"a":1}
|
||||
|
||||
{a,b,(.d):.a,e:.b}
|
||||
{"a":1, "b":2, "c":3, "d":"c"}
|
||||
{"a":1, "b":2, "c":1, "e":2}
|
||||
|
||||
{"a",b,"a$\(1+1)"}
|
||||
{"a":1, "b":2, "c":3, "a$2":4}
|
||||
{"a":1, "b":2, "a$2":4}
|
||||
|
||||
#
|
||||
# Field access, piping
|
||||
#
|
||||
|
||||
.foo
|
||||
{"foo": 42, "bar": 43}
|
||||
42
|
||||
|
||||
.foo | .bar
|
||||
{"foo": {"bar": 42}, "bar": "badvalue"}
|
||||
42
|
||||
|
||||
.foo.bar
|
||||
{"foo": {"bar": 42}, "bar": "badvalue"}
|
||||
42
|
||||
|
||||
.foo_bar
|
||||
{"foo_bar": 2}
|
||||
2
|
||||
|
||||
.["foo"].bar
|
||||
{"foo": {"bar": 42}, "bar": "badvalue"}
|
||||
42
|
||||
|
||||
|
||||
#
|
||||
# Multiple outputs, iteration
|
||||
#
|
||||
|
||||
.[]
|
||||
[1,2,3]
|
||||
1
|
||||
2
|
||||
3
|
||||
|
||||
1,1
|
||||
[]
|
||||
1
|
||||
1
|
||||
|
||||
1,.
|
||||
[]
|
||||
1
|
||||
[]
|
||||
|
||||
[.]
|
||||
[2]
|
||||
[[2]]
|
||||
|
||||
[[2]]
|
||||
[3]
|
||||
[[2]]
|
||||
|
||||
[{}]
|
||||
[2]
|
||||
[{}]
|
||||
|
||||
[.[]]
|
||||
["a"]
|
||||
["a"]
|
||||
|
||||
[(.,1),((.,.[]),(2,3))]
|
||||
["a","b"]
|
||||
[["a","b"],1,["a","b"],"a","b",2,3]
|
||||
|
||||
[([5,5][]),.,.[]]
|
||||
[1,2,3]
|
||||
[5,5,[1,2,3],1,2,3]
|
||||
|
||||
{x: (1,2)},{x:3} | .x
|
||||
null
|
||||
1
|
||||
2
|
||||
3
|
||||
|
||||
#
|
||||
# Slices
|
||||
#
|
||||
|
||||
[.[3:2], .[-5:4], .[:-2], .[-2:], .[3:3][1:], .[10:]]
|
||||
[0,1,2,3,4,5,6]
|
||||
[[], [2,3], [0,1,2,3,4], [5,6], [], []]
|
||||
|
||||
del(.[2:4],.[0],.[-2:])
|
||||
[0,1,2,3,4,5,6,7]
|
||||
[1,4,5]
|
||||
|
||||
.[2:4] = ([], ["a","b"], ["a","b","c"])
|
||||
[0,1,2,3,4,5,6,7]
|
||||
[0,1,4,5,6,7]
|
||||
[0,1,"a","b",4,5,6,7]
|
||||
[0,1,"a","b","c",4,5,6,7]
|
||||
|
||||
|
||||
#
|
||||
# Variables
|
||||
#
|
||||
|
||||
1 as $x | 2 as $y | [$x,$y,$x]
|
||||
null
|
||||
[1,2,1]
|
||||
|
||||
[1,2,3][] as $x | [[4,5,6,7][$x]]
|
||||
null
|
||||
[5]
|
||||
[6]
|
||||
[7]
|
||||
|
||||
42 as $x | . | . | . + 432 | $x + 1
|
||||
34324
|
||||
43
|
||||
|
||||
1 as $x | [$x,$x,$x as $x | $x]
|
||||
null
|
||||
[1,1,1]
|
||||
|
||||
# [.,(.[] | {x:.},.),.,.[]]
|
||||
|
||||
#
|
||||
# Builtin functions
|
||||
#
|
||||
|
||||
1+1
|
||||
null
|
||||
2
|
||||
|
||||
1+1
|
||||
"wtasdf"
|
||||
2.0
|
||||
|
||||
2-1
|
||||
null
|
||||
1
|
||||
|
||||
2-(-1)
|
||||
null
|
||||
3
|
||||
|
||||
1e+0+0.001e3
|
||||
"I wonder what this will be?"
|
||||
20e-1
|
||||
|
||||
.+4
|
||||
15
|
||||
19.0
|
||||
|
||||
.+null
|
||||
{"a":42}
|
||||
{"a":42}
|
||||
|
||||
null+.
|
||||
null
|
||||
null
|
||||
|
||||
.a+.b
|
||||
{"a":42}
|
||||
42
|
||||
|
||||
[1,2,3] + [.]
|
||||
null
|
||||
[1,2,3,null]
|
||||
|
||||
{"a":1} + {"b":2} + {"c":3}
|
||||
"asdfasdf"
|
||||
{"a":1, "b":2, "c":3}
|
||||
|
||||
"asdf" + "jkl;" + . + . + .
|
||||
"some string"
|
||||
"asdfjkl;some stringsome stringsome string"
|
||||
|
||||
"\u0000\u0020\u0000" + .
|
||||
"\u0000\u0020\u0000"
|
||||
"\u0000 \u0000\u0000 \u0000"
|
||||
|
||||
42 - .
|
||||
11
|
||||
31
|
||||
|
||||
[1,2,3,4,1] - [.,3]
|
||||
1
|
||||
[2,4]
|
||||
|
||||
[10 * 20, 20 / .]
|
||||
4
|
||||
[200, 5]
|
||||
|
||||
1 + 2 * 2 + 10 / 2
|
||||
null
|
||||
10
|
||||
|
||||
[16 / 4 / 2, 16 / 4 * 2, 16 - 4 - 2, 16 - 4 + 2]
|
||||
null
|
||||
[2, 8, 10, 14]
|
||||
|
||||
1 + tonumber + ("10" | tonumber)
|
||||
4
|
||||
15
|
||||
|
||||
[{"a":42},.object,10,.num,false,true,null,"b",[1,4]] | .[] as $x | [$x == .[]]
|
||||
{"object": {"a":42}, "num":10.0}
|
||||
[true, true, false, false, false, false, false, false, false]
|
||||
[true, true, false, false, false, false, false, false, false]
|
||||
[false, false, true, true, false, false, false, false, false]
|
||||
[false, false, true, true, false, false, false, false, false]
|
||||
[false, false, false, false, true, false, false, false, false]
|
||||
[false, false, false, false, false, true, false, false, false]
|
||||
[false, false, false, false, false, false, true, false, false]
|
||||
[false, false, false, false, false, false, false, true, false]
|
||||
[false, false, false, false, false, false, false, false, true ]
|
||||
|
||||
[.[] | length]
|
||||
[[], {}, [1,2], {"a":42}, "asdf", "\u03bc"]
|
||||
[0, 0, 2, 1, 4, 1]
|
||||
|
||||
map(keys)
|
||||
[{}, {"abcd":1,"abc":2,"abcde":3}, {"x":1, "z": 3, "y":2}]
|
||||
[[], ["abc","abcd","abcde"], ["x","y","z"]]
|
||||
|
||||
[1,2,empty,3,empty,4]
|
||||
null
|
||||
[1,2,3,4]
|
||||
|
||||
map(add)
|
||||
[[], [1,2,3], ["a","b","c"], [[3],[4,5],[6]], [{"a":1}, {"b":2}, {"a":3}]]
|
||||
[null, 6, "abc", [3,4,5,6], {"a":3, "b": 2}]
|
||||
|
||||
#
|
||||
# User-defined functions
|
||||
# Oh god.
|
||||
#
|
||||
|
||||
def f: . + 1; def g: def g: . + 100; f | g | f; (f | g), g
|
||||
3.0
|
||||
106.0
|
||||
105.0
|
||||
|
||||
def f: (1000,2000); f
|
||||
123412345
|
||||
1000
|
||||
2000
|
||||
|
||||
([1,2] + [4,5])
|
||||
[1,2,3]
|
||||
[1,2,4,5]
|
||||
|
||||
true
|
||||
[1]
|
||||
true
|
||||
|
||||
null,1,null
|
||||
"hello"
|
||||
null
|
||||
1
|
||||
null
|
||||
|
||||
[1,2,3]
|
||||
[5,6]
|
||||
[1,2,3]
|
||||
|
||||
def f(x): x | x; f([.], . + [42])
|
||||
[1,2,3]
|
||||
[[[1,2,3]]]
|
||||
[[1,2,3],42]
|
||||
[[1,2,3,42]]
|
||||
[1,2,3,42,42]
|
||||
|
||||
# test closures and lexical scoping
|
||||
def id(x):x; 2000 as $x | def f(x):1 as $x | id([$x, x, x]); def g(x): 100 as $x | f($x,$x+x); g($x)
|
||||
"more testing"
|
||||
[1,100,2100.0,100,2100.0]
|
||||
|
||||
# test backtracking through function calls and returns
|
||||
# this test is *evil*
|
||||
[[20,10][1,0] as $x | def f: (100,200) as $y | def g: [$x + $y, .]; . + $x | g; f[0] | [f][0][1] | f]
|
||||
999999999
|
||||
[[110.0, 130.0], [210.0, 130.0], [110.0, 230.0], [210.0, 230.0], [120.0, 160.0], [220.0, 160.0], [120.0, 260.0], [220.0, 260.0]]
|
||||
|
||||
# test recursion
|
||||
def fac: if . == 1 then 1 else . * (. - 1 | fac) end; [.[] | fac]
|
||||
[1,2,3,4]
|
||||
[1,2,6,24]
|
||||
|
||||
# test stack overflow and reallocation
|
||||
# this test is disabled for now, it takes a realllllly long time.
|
||||
# def f: if length > 1000 then . else .+[1]|f end; f | length
|
||||
# []
|
||||
# 1001
|
||||
|
||||
reduce .[] as $x (0; . + $x)
|
||||
[1,2,4]
|
||||
7
|
||||
|
||||
#
|
||||
# Paths
|
||||
#
|
||||
|
||||
path(.foo[0,1])
|
||||
null
|
||||
["foo", 0]
|
||||
["foo", 1]
|
||||
|
||||
path(.[] | select(.>3))
|
||||
[1,5,3]
|
||||
[1]
|
||||
|
||||
path(.)
|
||||
42
|
||||
[]
|
||||
|
||||
["foo",1] as $p | getpath($p), setpath($p; 20), delpaths([$p])
|
||||
{"bar": 42, "foo": ["a", "b", "c", "d"]}
|
||||
"b"
|
||||
{"bar": 42, "foo": ["a", 20, "c", "d"]}
|
||||
{"bar": 42, "foo": ["a", "c", "d"]}
|
||||
|
||||
map(getpath([2])), map(setpath([2]; 42)), map(delpaths([[2]]))
|
||||
[[0], [0,1], [0,1,2]]
|
||||
[null, null, 2]
|
||||
[[0,null,42], [0,1,42], [0,1,42]]
|
||||
[[0], [0,1], [0,1]]
|
||||
|
||||
map(delpaths([[0,"foo"]]))
|
||||
[[{"foo":2, "x":1}], [{"bar":2}]]
|
||||
[[{"x":1}], [{"bar":2}]]
|
||||
|
||||
["foo",1] as $p | getpath($p), setpath($p; 20), delpaths([$p])
|
||||
{"bar":false}
|
||||
null
|
||||
{"bar":false, "foo": [null, 20]}
|
||||
{"bar":false}
|
||||
|
||||
delpaths([[-200]])
|
||||
[1,2,3]
|
||||
[1,2,3]
|
||||
|
||||
del(.), del(empty), del((.foo,.bar,.baz) | .[2,3,0]), del(.foo[0], .bar[0], .foo, .baz.bar[0].x)
|
||||
{"foo": [0,1,2,3,4], "bar": [0,1]}
|
||||
null
|
||||
{"foo": [0,1,2,3,4], "bar": [0,1]}
|
||||
{"foo": [1,4], "bar": [1]}
|
||||
{"bar": [1]}
|
||||
|
||||
#
|
||||
# Assignment
|
||||
#
|
||||
.message = "goodbye"
|
||||
{"message": "hello"}
|
||||
{"message": "goodbye"}
|
||||
|
||||
.foo = .bar
|
||||
{"bar":42}
|
||||
{"foo":42, "bar":42}
|
||||
|
||||
.foo |= .+1
|
||||
{"foo": 42}
|
||||
{"foo": 43}
|
||||
|
||||
.[] += 2, .[] *= 2, .[] -= 2, .[] /= 2
|
||||
[1,3,5]
|
||||
[3,5,7]
|
||||
[2,6,10]
|
||||
[-1,1,3]
|
||||
[0.5, 1.5, 2.5]
|
||||
|
||||
.foo += .foo
|
||||
{"foo":2}
|
||||
{"foo":4}
|
||||
|
||||
.[0].a |= {"old":., "new":(.+1)}
|
||||
[{"a":1,"b":2}]
|
||||
[{"a":{"old":1, "new":2},"b":2}]
|
||||
|
||||
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}]
|
||||
|
||||
.[2][3] = 1
|
||||
[4]
|
||||
[4, null, [null, null, null, 1]]
|
||||
|
||||
.foo[2].bar = 1
|
||||
{"foo":[11], "bar":42}
|
||||
{"foo":[11,null,{"bar":1}], "bar":42}
|
||||
|
||||
#
|
||||
# Conditionals
|
||||
#
|
||||
|
||||
[.[] | if .foo then "yep" else "nope" end]
|
||||
[{"foo":0},{"foo":1},{"foo":[]},{"foo":true},{"foo":false},{"foo":null},{"foo":"foo"},{}]
|
||||
["yep","yep","yep","yep","nope","nope","yep","nope"]
|
||||
|
||||
[.[] | if .baz then "strange" elif .foo then "yep" else "nope" end]
|
||||
[{"foo":0},{"foo":1},{"foo":[]},{"foo":true},{"foo":false},{"foo":null},{"foo":"foo"},{}]
|
||||
["yep","yep","yep","yep","nope","nope","yep","nope"]
|
||||
|
||||
|
||||
# FIXME: define/test behaviour of 'if (.foo,.bar) then A else B end'
|
||||
|
||||
[.[] | [.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]]
|
||||
|
||||
.[] //= .[0]
|
||||
["hello",true,false,[false],null]
|
||||
["hello",true,"hello",[false],"hello"]
|
||||
|
||||
.[] | [.[0] and .[1], .[0] or .[1]]
|
||||
[[true,[]], [false,1], [42,null], [null,false]]
|
||||
[true,true]
|
||||
[false,true]
|
||||
[false,true]
|
||||
[false,false]
|
||||
|
||||
[.[] | not]
|
||||
[1,0,false,null,true,"hello"]
|
||||
[false,false,true,true,false,false]
|
||||
|
||||
# Check numeric comparison binops
|
||||
[10 > 0, 10 > 10, 10 > 20, 10 < 0, 10 < 10, 10 < 20]
|
||||
{}
|
||||
[true,false,false,false,false,true]
|
||||
|
||||
[10 >= 0, 10 >= 10, 10 >= 20, 10 <= 0, 10 <= 10, 10 <= 20]
|
||||
{}
|
||||
[true,true,false,false,true,true]
|
||||
|
||||
# And some in/equality tests
|
||||
[ 10 == 10, 10 != 10, 10 != 11, 10 == 11]
|
||||
{}
|
||||
[true,false,true,false]
|
||||
|
||||
["hello" == "hello", "hello" != "hello", "hello" == "world", "hello" != "world" ]
|
||||
{}
|
||||
[true,false,false,true]
|
||||
|
||||
[[1,2,3] == [1,2,3], [1,2,3] != [1,2,3], [1,2,3] == [4,5,6], [1,2,3] != [4,5,6]]
|
||||
{}
|
||||
[true,false,false,true]
|
||||
|
||||
[{"foo":42} == {"foo":42},{"foo":42} != {"foo":42}, {"foo":42} != {"bar":42}, {"foo":42} == {"bar":42}]
|
||||
{}
|
||||
[true,false,true,false]
|
||||
|
||||
# ugly complicated thing
|
||||
[{"foo":[1,2,{"bar":18},"world"]} == {"foo":[1,2,{"bar":18},"world"]},{"foo":[1,2,{"bar":18},"world"]} == {"foo":[1,2,{"bar":19},"world"]}]
|
||||
{}
|
||||
[true,false]
|
||||
|
||||
# containment operator
|
||||
[("foo" | contains("foo")), ("foobar" | contains("foo")), ("foo" | contains("foobar"))]
|
||||
{}
|
||||
[true, true, false]
|
||||
|
||||
map(.[1] as $needle | .[0] | contains($needle))
|
||||
[[[],[]], [[1,2,3], [1,2]], [[1,2,3], [3,1]], [[1,2,3], [4]], [[1,2,3], [1,4]]]
|
||||
[true, true, true, false, false]
|
||||
|
||||
map(.[1] as $needle | .[0] | contains($needle))
|
||||
[[["foobar", "foobaz"], ["baz", "bar"]], [["foobar", "foobaz"], ["foo"]], [["foobar", "foobaz"], ["blap"]]]
|
||||
[true, true, false]
|
||||
|
||||
[({foo: 12, bar:13} | contains({foo: 12})), ({foo: 12} | contains({})), ({foo: 12, bar:13} | contains({baz:14}))]
|
||||
{}
|
||||
[true, true, false]
|
||||
|
||||
{foo: {baz: 12, blap: {bar: 13}}, bar: 14} | contains({bar: 14, foo: {blap: {}}})
|
||||
{}
|
||||
true
|
||||
|
||||
{foo: {baz: 12, blap: {bar: 13}}, bar: 14} | contains({bar: 14, foo: {blap: {bar: 14}}})
|
||||
{}
|
||||
false
|
||||
|
||||
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}]
|
||||
|
||||
(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]
|
||||
[1,2,3,5]
|
||||
|
||||
[min, max, min_by(.[1]), max_by(.[1]), min_by(.[2]), max_by(.[2])]
|
||||
[[4,2,"a"],[3,1,"a"],[2,4,"a"],[1,3,"a"]]
|
||||
[[1,3,"a"],[4,2,"a"],[3,1,"a"],[2,4,"a"],[4,2,"a"],[1,3,"a"]]
|
||||
|
||||
[min,max,min_by(.),max_by(.)]
|
||||
[]
|
||||
[null,null,null,null]
|
||||
|
||||
.foo[.baz]
|
||||
{"foo":{"bar":4},"baz":"bar"}
|
||||
4
|
||||
|
||||
.[] | .error = "no, it's OK"
|
||||
[{"error":true}]
|
||||
{"error": "no, it's OK"}
|
||||
|
||||
[{a:1}] | .[] | .a=999
|
||||
null
|
||||
{"a": 999}
|
||||
+1231
File diff suppressed because it is too large.
Load diff
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
. "${0%/*}/setup"
|
||||
|
||||
$VALGRIND $Q $JQ -L "$mods" --run-tests $JQTESTDIR/jq.test
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
. "${0%/*}/setup"
|
||||
|
||||
# We set PAGER because there's a mantest for `env` that uses it.
|
||||
(cd $JQBASEDIR/docs && rake mantests) |
|
||||
env PAGER=less $VALGRIND $Q $JQ -L "$mods" --run-tests
|
||||
@@ -0,0 +1,5 @@
|
||||
def foo: "baz";
|
||||
def f: "wat";
|
||||
def f: "foo";
|
||||
def g: "bar";
|
||||
def fg: f+g;
|
||||
@@ -0,0 +1,2 @@
|
||||
module {version:1.7};
|
||||
def a: "a";
|
||||
@@ -0,0 +1,2 @@
|
||||
def a: "b";
|
||||
def b: "c";
|
||||
@@ -0,0 +1,16 @@
|
||||
module {whatever:null};
|
||||
import "a" as foo;
|
||||
import "d" as d {search:"./"};
|
||||
import "d" as d2{search:"./"};
|
||||
import "e" as e {search:"./../lib/jq"};
|
||||
import "f" as f {search:"./../lib/jq"};
|
||||
import "data" as $d;
|
||||
def a: 0;
|
||||
def c:
|
||||
if $d::d[0] != {this:"is a test",that:"is too"} then error("data import is busted")
|
||||
elif d2::meh != d::meh then error("import twice doesn't work")
|
||||
elif foo::a != "a" then error("foo::a didn't work as expected")
|
||||
elif d::meh != "meh" then error("d::meh didn't work as expected")
|
||||
elif e::bah != "bah" then error("e::bah didn't work as expected")
|
||||
elif f::f != "f is here" then error("f::f didn't work as expected")
|
||||
else foo::a + "c" + d::meh + e::bah end;
|
||||
@@ -0,0 +1 @@
|
||||
def meh: "meh";
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"this": "is a test",
|
||||
"that": "is too"
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
def bah: "bah";
|
||||
@@ -0,0 +1 @@
|
||||
def f: "f is here";
|
||||
Loaded 100 of 114 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user